这是自定义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. git提交时忽略指定文件

    git提交时忽略指定文件 我们在项目开发过程中经常用到git来管理自己的项目,使用git版本控制进行多人协作开发具有许多优势,这里就不一一阐述了,有兴趣的同学可以自己去查找资料进行系统的学习.而本篇文 ...

  2. C语言基于NIOSII的软件开发及流水灯设计

    一.Quartus II 12.1 (32-Bit)进行硬件设计 1.所需要的系统元器件组成 2.系统电路图 二.Nios II 12.1 Software Build Tools for Eclip ...

  3. unittest---unittest封装方法

    前面我们写了一个关于查询歌曲的接口测试,但是代码重复性比较大,进行一次简单的优化 封装方法 在编写自动化脚本的时候,都要求代码简介,上一篇unittest---unittest断言中代码重复性比较多, ...

  4. 集合系列 Queue(十):LinkedList

    我们之前在说到 List 集合的时候已经说过 LinkedList 了.但 LinkedList 不仅仅是一个 List 集合实现,其还是一个双向队列实现. public class LinkedLi ...

  5. 【BZOJ 2138】stone

    Problem Description 话说 \(Nan\) 在海边等人,预计还要等上 \(M\) 分钟.为了打发时间,他玩起了石子. \(Nan\) 搬来了 \(N\) 堆石子,编号为 \(1\) ...

  6. (转)Python中的常见特殊方法—— repr方法

    原文链接:https://www.cnblogs.com/tizer/p/11178473.html 在Python中有些方法名.属性名的前后都添加了双下划线,这种方法.属性通常都属于Python的特 ...

  7. SpringBoot 并发登录人数控制

    通常系统都会限制同一个账号的登录人数,多人登录要么限制后者登录,要么踢出前者,Spring Security 提供了这样的功能,本文讲解一下在没有使用Security的时候如何手动实现这个功能 dem ...

  8. 关于js的for in循环,慎用

    参考:http://www.cftea.com/c/2014/08/6290.asp作者:vkvi 如题我看到也有点诧异,测试了真的有这个问题,上代码 Array.prototype.a = func ...

  9. DomDom: 1 Vulnhub Walkthrough

    主机层面扫描: ╰─ nmap -p1-65535 -A -sV 10.10.202.140 You name 存在XSS 漏洞 右键源码有隐藏form表单 修改其type属性为:text 尝试了SQ ...

  10. dedecmsV5.7 后台上传m4a的音频之后不展示

    问题:dedecmsV5.7 在后台上传了m4a的音频文件(如何添加m4a的音频格式,更改系统-系统基本配置-附件设置)之后,列表里不展示,如图: 解决方案: 打开include/dialog/sel ...