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

二. 自定义View
1. 自定义需要用到的属性
首先,我们创建一个继承于LinearLayout的PageController控件。然后在其中定义需要用到的属性。
| 12
 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文件用来自定义属性。
| 12
 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. 创建构造方法
由于有四个构造方法,所以我们需要创建一个公共的方法用来初始化一些公共的东西。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 
 
 private void init() {
 
 this.setOrientation(LinearLayout.HORIZONTAL);
 
 
 this.setGravity(Gravity.CENTER);
 }
 
 | 
java代码创建该类的构造方法不用多做什么,我们将大部分的任务交给定义的属性的set方法。xml代码创建该类的构造方法需要解析自定义的属性值。
| 12
 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中。
| 12
 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。
| 12
 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;
 }
 
 | 
在此方法中是将新的页面控制点替换掉旧的页面控制点,所以我们可以获取这两个控制点进而设置相关显示的效果。
| 12
 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. 设置页面回调
创建用于回调当前页面值的接口,我们在监听值改变的类中实现该接口,实现对应的方法,并设置监听者是自己。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 
 
 public interface PageChangeListener {
 
 void pageHasChange(int currentPage);
 }
 
 
 private PageChangeListener pageChangeListener;
 
 | 
7. 设置页面切换动画
创建页面切换动画的接口,在其中定义切换动画的方法。将该方法交给外部来具体实现。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 
 
 public interface PageChangeAnimation {
 
 void changeAnimation(View last_dot, View current_dot);
 }
 
 
 private PageChangeAnimation pageChangeAnimation;
 
 | 
三. 具体的使用
1. 准备页面控制点的样式
| 12
 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. 监听类实现回调接口以及设置切换动画
| 12
 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。


| 12
 3
 4
 
 | //实际使用需要写成这样dependencies {
 implementation 'com.github.xiaoshitounen:PageController:1.0.0'
 }
 
 | 
5. 注意
在项目中同步的时候可能会报错:
| 12
 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 的最佳实践