这是自定义View系列的第二篇博客,我们继续来学习关于自定义View的知识。

今天我们来实现一下广告条案例。

我们要实现的是这样的一个效果。



要想实现这样的效果,我们可以借助ViewPager控件,然后加上自定义的一些控件即可完成。那么现在就开始吧。

新建一个android项目。

修改activity_main.xml文件。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.itcast.test0429.MainActivity"> <android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="180dp"/> <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/viewpager"
android:background="#44000000"
android:orientation="vertical"
android:padding="5dp"> <TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="3dp"
android:text="复仇者联盟4"
android:textColor="#ffffff" /> <LinearLayout
android:id="@+id/ll_point_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal"> </LinearLayout>
</LinearLayout> </RelativeLayout>

然后修改MainActivity的代码。

package com.itcast.test0429;

import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList; import butterknife.BindInt;
import butterknife.BindView;
import butterknife.ButterKnife; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName();
@BindView(R.id.viewpager)
ViewPager viewpager;
@BindView(R.id.tv_title)
TextView tvTitle;
@BindView(R.id.ll_point_group)
LinearLayout llPointGroup; private ArrayList<ImageView> imageViews; //图片资源ID
private final int[] imageIds = {
R.drawable.b1,
R.drawable.b2,
R.drawable.b3
}; //图片标题集合
private final String[] imageDescriptions = {
"标题一",
"标题二",
"标题三"
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this); imageViews = new ArrayList<>();
for(int i = 0;i < imageIds.length;i++){
ImageView imageView = new ImageView(this);
imageView.setBackgroundResource(imageIds[i]); //添加到集合中
imageViews.add(imageView);
} //设置适配器
viewpager.setAdapter(new MyAdapter());
} class MyAdapter extends PagerAdapter{ /**
* 得到图片的总数
* @return
*/
@Override
public int getCount() {
return imageViews.size();
} /**
* 相当于getView方法
* @param container ViewPager自身
* @param position 当前实例化页面的位置
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = imageViews.get(position);
container.addView(imageView);//添加到ViewPager中
Log.e(TAG,"instantiateItem==" + position + ",---imageView==" + imageView);
return imageView;
} /**
* 比较view和object是否同一个实例
* @param view 页面
* @param object instantiateItem方法返回的结果
* @return
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
} /**
* 释放资源
* @param container ViewPager
* @param position 要释放的位置
* @param object 要释放的页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.e(TAG,"destroyItem==" + position + ",---object==" + object);
container.removeView((View) object);
}
}
}

需要注意的就是PageAdapter的使用,每个方法我都写了注释,大家应该能理解吧。ViewPager在运行之后总共会初始化两个页面,最多初始化三个,随着页面的增多,ViewPager会自动销毁前面的页面进而提供给后面的页面使用,这是ViewPage的内容优化,我们可以来验证一下,我在初始化和销毁的方法中都打印了日志。现在,我们来运行项目。



我们看看日志情况。



只打印了两次初始化信息,说明ViewPager只初始化了两个页面。

我们向左滑动ViewPager来切换页面,此时观察日志信息。



会发现在创建了三个实例后,第一个页面的实例就被销毁了,这就证实了刚才的结论。

这样我们第一阶段的编码就完成了,接下来我们实现添加指示点,并根据页面改变设置文本。

而指示点有多种实现方式,可以通过图片来显示,也可以自己绘制指示点显示,我这种用的是第二种方式。贴出MainActivity的代码。

package com.itcast.test0429;

import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import java.util.ArrayList; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnPageChange; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName();
@BindView(R.id.viewpager)
ViewPager viewpager;
@BindView(R.id.tv_title)
TextView tvTitle;
@BindView(R.id.ll_point_group)
LinearLayout llPointGroup; private ArrayList<ImageView> imageViews; //图片资源ID
private final int[] imageIds = {
R.drawable.b1,
R.drawable.b2,
R.drawable.b3,
R.drawable.b1,
R.drawable.b2,
R.drawable.b3
}; /**
* 上一次高亮显示的位置
*/
private int prePosition = 0; //图片标题集合
private final String[] imageDescriptions = {
"标题一",
"标题二",
"标题三",
"标题四",
"标题五",
"标题六"
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this); imageViews = new ArrayList<>();
for (int i = 0; i < imageIds.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setBackgroundResource(imageIds[i]); //添加到集合中
imageViews.add(imageView); //添加点
ImageView point = new ImageView(this);
point.setBackgroundResource(R.drawable.point_selector);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(8, 8); if (i == 0) {
point.setEnabled(true);//显示红色
} else {
point.setEnabled(false);//显示灰色
params.leftMargin = 8;
} point.setLayoutParams(params); llPointGroup.addView(point);
} //设置适配器
viewpager.setAdapter(new MyAdapter()); tvTitle.setText(imageDescriptions[prePosition]); //设置监听ViewPager页面的改变
viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { /**
* 当页面滚动的时候回调此方法
* @param position 当前页面的位置
* @param positionOffset 滑动页面的百分比
* @param positionOffsetPixels 在屏幕上滑动的像素
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } /**
* 当某个页面被选中的时候回调此方法
* @param position 被选中页面的位置
*/
@Override
public void onPageSelected(int position) {
//设置对应页面的文本信息
tvTitle.setText(imageDescriptions[position]);
//把上一个高亮的设置为默认-灰色
llPointGroup.getChildAt(prePosition).setEnabled(false);
//当前设置为高亮-红色
llPointGroup.getChildAt(position).setEnabled(true); prePosition = position;
} /**
* 当页面滚动状态变化的时候回调此方法
* 静止-》滑动
* 滑动--》静止
* 静止---》拖拽
* @param state
*/
@Override
public void onPageScrollStateChanged(int state) { }
});
} class MyAdapter extends PagerAdapter { /**
* 得到图片的总数
*
* @return
*/
@Override
public int getCount() {
return imageViews.size();
} /**
* 相当于getView方法
*
* @param container ViewPager自身
* @param position 当前实例化页面的位置
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView imageView = imageViews.get(position);
container.addView(imageView);//添加到ViewPager中
Log.e(TAG, "instantiateItem==" + position + ",---imageView==" + imageView);
return imageView;
} /**
* 比较view和object是否同一个实例
*
* @param view 页面
* @param object instantiateItem方法返回的结果
* @return
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
} /**
* 释放资源
*
* @param container ViewPager
* @param position 要释放的位置
* @param object 要释放的页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.e(TAG, "destroyItem==" + position + ",---object==" + object);
container.removeView((View) object);
}
}
}

指示点我是用xml文件绘制的,point_selecotr.xml文件代码如下。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:drawable="@drawable/point_normal"/>
<item android:state_enabled="true" android:drawable="@drawable/point_press"/>
</selector>

point_normal.xml文件代码如下。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="8dp"
android:height="8dp" />
<solid android:color="#44000000" />
</shape>

point_press.xml文件代码如下。

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="8dp"
android:height="8dp" />
<solid android:color="#ff0000" />
</shape>

我们运行项目,预览效果。



这个时候,指示点和文本标题的内容都随着我们的滑动而改变,这样,我们的目的就达到了。

我们来实现第三阶段的需求,支持左右无限滑动。

怎么才能实现这个需求呢?滑动的页面数量是由适配器的getCount方法决定的,所以,我们在getCount方法里直接返回Integer.MAX_VALUE,这是int的最大值,这个数量已经非常庞大了,可以说近似于无限滑动,但是设置如此大的数量,而我们的数据又没有这么多,那么在滑动页面的时候肯定会产生索引越界的问题,所以,为了避免这样的问题产生,我们就必须把有页面位置的地方全部取模处理,让其保持在我们的有限数据范围内,这样就能够实现我们的需求了。那么我们来修改MainActivity的代码。

package com.itcast.test0429;

import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import java.util.ArrayList; import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnPageChange; public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName();
@BindView(R.id.viewpager)
ViewPager viewpager;
@BindView(R.id.tv_title)
TextView tvTitle;
@BindView(R.id.ll_point_group)
LinearLayout llPointGroup; private ArrayList<ImageView> imageViews; //图片资源ID
private final int[] imageIds = {
R.drawable.b1,
R.drawable.b2,
R.drawable.b3,
R.drawable.b1,
R.drawable.b2,
R.drawable.b3
}; /**
* 上一次高亮显示的位置
*/
private int prePosition = 0; //图片标题集合
private final String[] imageDescriptions = {
"标题一",
"标题二",
"标题三",
"标题四",
"标题五",
"标题六"
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this); imageViews = new ArrayList<>();
for (int i = 0; i < imageIds.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setBackgroundResource(imageIds[i]); //添加到集合中
imageViews.add(imageView); //添加点
ImageView point = new ImageView(this);
point.setBackgroundResource(R.drawable.point_selector);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(8, 8); if (i == 0) {
point.setEnabled(true);//显示红色
} else {
point.setEnabled(false);//显示灰色
params.leftMargin = 8;
} point.setLayoutParams(params); llPointGroup.addView(point);
} //设置适配器
viewpager.setAdapter(new MyAdapter()); tvTitle.setText(imageDescriptions[prePosition]); //设置监听ViewPager页面的改变
viewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { /**
* 当页面滚动的时候回调此方法
* @param position 当前页面的位置
* @param positionOffset 滑动页面的百分比
* @param positionOffsetPixels 在屏幕上滑动的像素
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } /**
* 当某个页面被选中的时候回调此方法
* @param position 被选中页面的位置
*/
@Override
public void onPageSelected(int position) { int realPosition = position % imageViews.size(); //设置对应页面的文本信息
tvTitle.setText(imageDescriptions[realPosition]);
//把上一个高亮的设置为默认-灰色
llPointGroup.getChildAt(prePosition).setEnabled(false);
//当前设置为高亮-红色
llPointGroup.getChildAt(realPosition).setEnabled(true); prePosition = realPosition;
} /**
* 当页面滚动状态变化的时候回调此方法
* 静止-》滑动
* 滑动--》静止
* 静止---》拖拽
* @param state
*/
@Override
public void onPageScrollStateChanged(int state) { }
});
} class MyAdapter extends PagerAdapter { /**
* 得到图片的总数
*
* @return
*/
@Override
public int getCount() {
return Integer.MAX_VALUE;
} /**
* 相当于getView方法
*
* @param container ViewPager自身
* @param position 当前实例化页面的位置
* @return
*/
@Override
public Object instantiateItem(ViewGroup container, int position) { int realPosition = position % imageViews.size(); ImageView imageView = imageViews.get(realPosition);
container.addView(imageView);//添加到ViewPager中
Log.e(TAG, "instantiateItem==" + position + ",---imageView==" + imageView);
return imageView;
} /**
* 比较view和object是否同一个实例
*
* @param view 页面
* @param object instantiateItem方法返回的结果
* @return
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
} /**
* 释放资源
*
* @param container ViewPager
* @param position 要释放的位置
* @param object 要释放的页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Log.e(TAG, "destroyItem==" + position + ",---object==" + object);
container.removeView((View) object);
}
}
}

这样就可以了,我们来运行一下项目。



乍一看,好像是没什么问题了,但是,这个程序是有bug的,只是这样我们没有测试出来,我再操作一遍。



发现问题了没有,我在最开始进入程序的时候,右滑是不是滑不动?因为ViewPager默认从0开始,所以左边已经没有其它页面了,故你无法右滑,那怎么解决这个问题呢?找到了问题的原因,那就可以有解决办法,既然左边没有页面,那就让它有页面不就得了?我们把第一张图定位到中间位置,那么它的左边和右边就都会拥有数量庞大的页面。虽然页面很多,但也不是滑不完的,假如有一个用户,他就是闲得慌,他就拼命地滑,结果,把左边或者右边的所有页面都滑完了,这种情况我们只能说这个人他真的是闲得慌了,总之,按照正常情况,如此多的页面时足够用户滑动了。

原理我们知道了,如何通过编码实现呢?很简单,只需要ViewPager设置适配器之后加上如下代码即可。

 //设置中间位置
int item = Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % imageViews.size();//要保证imageViews的整数倍
viewpager.setCurrentItem(item);

现在运行项目,预览效果。



这样问题就解决了。整个案例也就结束了,这只是ViewPager使用的一小部分,这个程序还可以加上很多的功能,比如自动播放,然后点击跳转等等,由于篇幅有限,我就不一一实现了,感兴趣的可以自己尝试着写一写。

源码已上传至GitHub

Android进阶之绘制-自定义View完全掌握(二)的更多相关文章

  1. Android进阶之绘制-自定义View完全掌握(四)

    前面的案例中我们都是使用系统的一些控件通过组合的方式来生成我们自定义的控件,自定义控件的实现还可以通过自定义类继承View来完成.从该篇博客开始,我们通过自定义类继承View来实现一些我们自定义的控件 ...

  2. Android进阶之绘制-自定义View完全掌握(一)

    Android的UI设计可以说是决定一个app质量的关键因素,因为人们在使用app的时候,最先映入眼帘的就是app的界面了,一个美观.充实的界面能够给用户带来非常好的体验,会在用户心中留下好的印象. ...

  3. Android进阶之绘制-自定义View完全掌握(三)

    自定义View系列的第三篇博客,我们来学习如何实现自定义下拉框. 今天的程序,我们来实现这样的一个效果. 布局非常简单,我们直接开始编码. 修改activity_main.xml文件的代码. < ...

  4. Android进阶之绘制-自定义View完全掌握(五)

    在自定义类继承View实现自定义控件的过程中,我们还应该对一些自定义属性有所了解. 我们通过一个案例来学习一下. 新建一个android项目,然后我们创建一个类MyAttributeView继承Vie ...

  5. Android进阶(十八)AndroidAPP开发问题汇总(二)

    Android进阶(十八)AndroidAPP开发问题汇总(二) 端口被占用解决措施: Android使用SimpleAdapter更新ListView里面的Drawable元素: http://ww ...

  6. Android显示框架:自定义View实践之绘制篇

    文章目录 一 View 二 Paint 2.1 颜色处理 2.2 文字处理 2.3 特殊处理 三 Canvas 3.1 界面绘制 3.2 范围裁切 3.3 集合变换 四 Path 4.1 添加图形 4 ...

  7. Android自定义View学习(二)

    绘制顺序 参考:HenCoder Android 开发进阶:自定义 View 1-5 绘制顺序 绘制过程 包括 背景 主体(onDraw()) 子 View(dispatchDraw()) 滑动边缘渐 ...

  8. 【Android - 进阶】之自定义视图浅析

    1       概述 Android自定义View / ViewGroup的步骤大致如下: 1) 自定义属性: 2) 选择和设置构造方法: 3) 重写onMeasure()方法: 4) 重写onDra ...

  9. 【Android 应用开发】自定义View 和 ViewGroup

    一. 自定义View介绍 自定义View时, 继承View基类, 并实现其中的一些方法. (1) ~ (2) 方法与构造相关 (3) ~ (5) 方法与组件大小位置相关 (6) ~ (9) 方法与触摸 ...

随机推荐

  1. 可编程实验板EPM1270T144C5使用说明

  2. Vue之循环遍历Json数据,填充Table表格

    简单记一次Vue循环遍历Json数据,然后填充到Table表格中,展示到前端的代码: async getData(id) { const res = await this.$store.api.new ...

  3. 基于titanic数据集预测titanic号旅客生还率

    数据清洗及可视化 实验内容 数据清洗是数据分析中非常重要的一部分,也最繁琐,做好这一步需要大量的经验和耐心.这门课程中,我将和大家一起,一步步完成这项工作.大家可以从这门课程中学习数据清洗的基本思路以 ...

  4. js Set对象

    1.将数组转换成Set对象 let arr1 = new Set([1,2,3,4]) console.log(arr1) //{1,2,3,4} 2.数组去重 let arr2 = new Set( ...

  5. 多次调用settimeout 如何使用单例模式

    <script> function aaa() { window.counter = window.counter||1; console.log(window.counter); win ...

  6. SpringBoot:@Scope注解学习

    概述 先通过注解的javadoc,可以了解到,@Scope在和@Component注解一起修饰在类上,作为类级别注解时,@Scope表示该类实例的范围,在和@Bean一起修饰在方法上,作为方法级别注解 ...

  7. [译]OpenSSL Cookbook

    记录个人学习过程吧,顺便翻译一下.另外,本文并不会包括原连接中的所有内容,仅包括个人在工作中会经常遇到的. 参考:OpenSSL Cookbook 前言 由于协议特性和实现的复杂性,有时很难确定安全服 ...

  8. 【xAsset框架】HFS 轻量级HTTP Server快速入门指南

    一.引子 最近马三有幸参与开发了一个简易轻量的Unity资源管理框架 xAsset ,xasset 提供了一种使用资源路径的简单的方式来加载资源,简化了Unity项目资源打包,更新,加载,和回收的作业 ...

  9. Spring中,关于IOC和AOP的那些事

    一.spring 的优点? 1.降低了组件之间的耦合性 ,实现了软件各层之间的解耦 2.可以使用容易提供的众多服务,如事务管理,消息服务等 3.容器提供单例模式支持 4.容器提供了AOP技术,利用它很 ...

  10. Response to 16岁的篮球投手

    关于篮球,我想写的很多,被偏爱的运动,被赞美的运动,带着青春的万丈光泽. 我们对易建联的苛刻是因为想当然,对大侄子的溺爱是因为急功近利.过于娱乐化和商业化,让一项竞技体育变得像是豆瓣八组的吃瓜盛宴. ...