一. 前言
Android中对于下面效果的页面控制器的应用场景还是比较多的,最典型的例子就是引导图和轮播图。接下来我们就通过自定义View完成下面的效果,并且做成静态库,方便日后使用。
Github地址:PageController

二. 自定义View
1. 自定义需要用到的属性
首先,我们创建一个继承于LinearLayout的PageController控件。然后在其中定义需要用到的属性。
1 2 3 4 5 6 7 8 9 10 11
| private int numberOfPage;
private int pagePadding;
private int currentPage;
private int pageResource;
|
2. 在values文件夹下自定义属性
由于定义的属性在系统默认的属性里面没有,所以我们在values文件夹下创建了xml文件用来自定义属性。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="PagerController"> <attr name="numberOfPage" format="integer"/> <attr name="currentPage" format="integer"/> <attr name="pagePadding" format="integer"/> <attr name="pageResource" format="reference"/> </declare-styleable> </resources>
|
3. 创建构造方法
由于有四个构造方法,所以我们需要创建一个公共的方法用来初始化一些公共的东西。
1 2 3 4 5 6 7 8 9 10
|
private void init() { this.setOrientation(LinearLayout.HORIZONTAL);
this.setGravity(Gravity.CENTER); }
|
java代码创建该类的构造方法不用多做什么,我们将大部分的任务交给定义的属性的set方法。xml代码创建该类的构造方法需要解析自定义的属性值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public PagerController(Context context, @Nullable AttributeSet attrs) { super(context, attrs);
init();
if (attrs != null){ TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PagerController);
pagePadding = typedArray.getInteger( R.styleable.PagerController_pagePadding, 20); pageResource = typedArray.getResourceId( R.styleable.PagerController_pageResource, 0); currentPage = typedArray.getInteger( R.styleable.PagerController_currentPage, 0); this.setNumberOfPage( typedArray.getInteger( R.styleable.PagerController_numberOfPage, 0) );
typedArray.recycle(); } }
|
4. 创建页面控制点
由于我们对于每一个页面控制点的大小都是设置的wrap_content,而我们又设置了BackgroundResource,所以页面控制点的大小在传入的pageResource中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public void setNumberOfPage(int numberOfPage) { this.numberOfPage = numberOfPage;
for (int i = 0; i < numberOfPage; i++) { ImageView dot = new ImageView(getContext()); dot.setBackgroundResource(this.getPageResource()); LayoutParams layoutParams = new LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ); if (i > 0) { layoutParams.leftMargin = this.getPagePadding(); } else { dot.setEnabled(false); }
this.addView(dot,layoutParams); } }
|
5. 设置当前页面以及左右切换
实现左右切换就是判断触摸点在视图的左半边还是右半边,然后确定好页面值,调用setCurrentPage。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
@SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { float x = event.getX();
if (x > this.getWidth() / 2.0){ this.setCurrentPage((currentPage+1) % numberOfPage); }else { this.setCurrentPage((currentPage-1+numberOfPage) % numberOfPage); } if (this.pageChangeListener != null) { pageChangeListener.pageHasChange(this.currentPage); } }
return true; }
|
在此方法中是将新的页面控制点替换掉旧的页面控制点,所以我们可以获取这两个控制点进而设置相关显示的效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public void setCurrentPage(int currentPage) { ImageView last_dot = (ImageView) this.getChildAt(this.currentPage); last_dot.setEnabled(true);
this.currentPage = currentPage;
ImageView current_dot = (ImageView) this.getChildAt(this.currentPage); current_dot.setEnabled(false);
if (this.pageChangeAnimation != null) { pageChangeAnimation.changeAnimation(last_dot, current_dot); } }
|
6. 设置页面回调
创建用于回调当前页面值的接口,我们在监听值改变的类中实现该接口,实现对应的方法,并设置监听者是自己。
1 2 3 4 5 6 7 8 9 10
|
public interface PageChangeListener { void pageHasChange(int currentPage); }
private PageChangeListener pageChangeListener;
|
7. 设置页面切换动画
创建页面切换动画的接口,在其中定义切换动画的方法。将该方法交给外部来具体实现。
1 2 3 4 5 6 7 8 9 10
|
public interface PageChangeAnimation { void changeAnimation(View last_dot, View current_dot); }
private PageChangeAnimation pageChangeAnimation;
|
三. 具体的使用
1. 准备页面控制点的样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"> <shape android:shape="oval"> <size android:width="40dp" android:height="40dp" />
<solid android:color="#ff0000"/> </shape> </item>
<item android:state_enabled="true"> <shape android:shape="oval"> <size android:width="40dp" android:height="40dp" />
<solid android:color="#666666"/> </shape> </item> </selector>
|
2. 监听类实现回调接口以及设置切换动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <?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=".MainActivity">
<swu.xl.pagecontroller.PagerController android:id="@+id/page_controller" android:layout_centerInParent="true" android:background="#000000" android:layout_width="match_parent" android:layout_height="wrap_content" app:numberOfPage="5" app:pageResource="@drawable/page_controller_shape" app:pagePadding="30" android:paddingTop="20dp" android:paddingBottom="20dp" />
</RelativeLayout>
public class MainActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
PagerController pagerController = findViewById(R.id.page_controller);
pagerController.setPageChangeAnimation(new PagerController.PageChangeAnimation() { @Override public void changeAnimation(View last_dot, View current_dot) {
ObjectAnimator scale = ObjectAnimator.ofFloat(current_dot, "scaleX", 1.0f, 1.2f, 1.0f); scale.setDuration(500); scale.setInterpolator(new BounceInterpolator()); scale.start(); } });
pagerController.setPageChangeListener(new PagerController.PageChangeListener() { @Override public void pageHasChange(int currentPage) { System.out.println("当前页面:"+(currentPage+1)); } }); } }
|
3. 运行效果

四. 制作静态库
详细步骤可以参考:android制作依赖库上传github并公开使用
1. 创建一个Android Library模块
创建一个Android Library模块,将在app模块测试的PageController类以及自定义属性的xml文件复制到新建的Android Library模块内。

2. 将项目提交到Github
使用Android Studio将该项目提交到Github上。
具体内容可以参考:AndroidStudio-提交&更新代码到Github上的远程仓库

3. 创建依赖库的版本进行发布
填写的 Tag Version 很重要,下一步会用到。


4. 获取依赖地址
打开 JitPack,输入Github项目地址,点击Look up,即可查到Library的版本号,也就是我们上一步填写的。往下浏览便可以看到如何添加依赖,记住需要替换其中的Tag。


1 2 3 4
| //实际使用需要写成这样 dependencies { implementation 'com.github.xiaoshitounen:PageController:1.0.0' }
|
5. 注意
在项目中同步的时候可能会报错:
1 2 3
| ERROR: Failed to resolve: XXX Show in Project Structure dialog Affected Modules: app
|
错误可能原因是:
① Tag值没有替换或者不是最新的。参考:AS(android studio) 添加第三方库时报,Error: Failed to resolve: com.github Affected Modules 解决办法
② 依赖位置添加错误,maven依赖需要放在allprojects中。参考:ERROR: Failed to resolve: com.github.lzyzsd:jsbridge:1.0.4 Show in Project Structure dialog Affected
③ 运行的版本和静态库额版本不一致。参考:Failed to resolve:com.android.support:appcompat-v7:报错处理
参考文章
实用——编写 Android Library 的最佳实践