简介
本文主要记录了学习自定义指示器的过程,从简单的组合控件到完全自定义View的实现。从中熟悉了view方面的知识。
现在各式各样的指示器很多,框架很多,炫酷的效果也很多。但是小白我在阅读大牛写的代码时,也很难读懂关键的操作,也尝试去看谷歌工程师写的控件,摄取到的知识还是有限,这也从侧面说明了,本人的自定义View技能很薄弱,加之一些常用的API掌握的不是很好,也为自己敲响了警钟了,所以我希望从点滴从前辈中学习简易的自定义view知识,而最最常见的是各种进度条以及知识器,本文是对指示器的积累,也是从很基础的开始,我现在追求的是实现一定的效果无法满足拓展性,一步一个台阶吧。也是慢慢的在这个过程中不断的完善,也主要是借鉴别人写的控件。
运行效果
从上至下为简易,自定义一般到比较不错的自定义效果。
1.通过RadioGroup+ImageView实现指示器滑动效果
如上效果,和TabLayout相类似,但是必然是比TabLayout差很多的。主要的思路是使用RadioGroup作文字的单选,通过ImageView的位置来指示选定了哪一个tab,这里主要通过ViewPager滑动来设置指示器的位置。在看代码前有些小的知识点需要关注。
1. setLayoutParams()
通过为某一个特定的View设置参数可以改变该控件的位置
public void setLayoutParams(ViewGroup.LayoutParams params) {
if (params == null) {
throw new NullPointerException("Layout parameters cannot be null");
}
mLayoutParams = params;
resolveLayoutParams();
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
}
requestLayout();
}
设置指示器的位置,我们需要获得指示器的params配置属性,该属性包决定了在父组件的位置信息,为指示器包一层线性布局,那么就可以设置左右的margin来达到滑动的效果。注意得强转为LinearLayout.LayoutParams,方能够设置leftMargin
//*****
// 只有其父布局为LinearLatout时,才能够有 LinearLayoutParams,才能够有leftMargin 设置左边间距
//*****
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mIvLinear.getLayoutParams();
layoutParams.width = lineWidth;
layoutParams.leftMargin = getScreenWidthWidth()/6 - lineWidth/2;
mIvLinear.setLayoutParams(layoutParams);
2.ViewPager
通过addOnPageChangedListener 可以监听页面的滑动,那有必要对下面的参数进行探讨
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.e("onPageScrolled",position+", "+positionOffset+", "+positionOffsetPixels);
}
界面1-界面2 log日志
从下面日志 我们可以发现第一个参数值直接从0->1,第二个参数值从0.0依次增加到0.9xx无限靠近1,然后页面到达第二页它又恢复成了0,第三个参数从1开始累积到719+(这个没有参考加之)
E/onPageScrolled: 0, 0.0, 0
E/onPageScrolled: 0, 0.0, 0
E/onPageScrolled: 0, 0.019444445, 14
E/onPageScrolled: 0, 0.045833334, 33
E/onPageScrolled: 0, 0.079166666, 57
E/onPageScrolled: 0, 0.10138889, 73
E/onPageScrolled: 0, 0.119444445, 86
E/onPageScrolled: 0, 0.13333334, 96
E/onPageScrolled: 0, 0.14861111, 107
E/onPageScrolled: 0, 0.15972222, 115
E/onPageScrolled: 0, 0.19305556, 139
.......
E/onPageScrolled: 0, 0.98055553, 706
E/onPageScrolled: 0, 0.9847222, 709
E/onPageScrolled: 0, 0.98888886, 712
E/onPageScrolled: 0, 0.9916667, 714
E/onPageScrolled: 0, 0.9930556, 715
E/onPageScrolled: 0, 0.99583334, 717
E/onPageScrolled: 0, 0.99722224, 718
E/onPageScrolled: 0, 0.9986111, 719
E/onPageScrolled: 1, 0.0, 0
界面2-界面1log日志
我们可以发现第一个参数从第二页的1瞬间变为了0,之后参数一一致停留在0,而参数二大致是从1渐变到0的
E/onPageScrolled: 1, 0.0, 0
E/onPageScrolled: 0, 0.99583334, 717
E/onPageScrolled: 0, 0.9736111, 701
E/onPageScrolled: 0, 0.9527778, 686
E/onPageScrolled: 0, 0.9291667, 669
E/onPageScrolled: 0, 0.90416664, 651
E/onPageScrolled: 0, 0.87777776, 632
E/onPageScrolled: 0, 0.84444445, 608
E/onPageScrolled: 0, 0.79305553, 571
E/onPageScrolled: 0, 0.75416666, 543
E/onPageScrolled: 0, 0.70555556, 508
........
E/onPageScrolled: 0, 0.015277778, 11
E/onPageScrolled: 0, 0.011111111, 8
E/onPageScrolled: 0, 0.0069444445, 5
E/onPageScrolled: 0, 0.0055555557, 4
E/onPageScrolled: 0, 0.004166667, 3
E/onPageScrolled: 0, 0.0027777778, 2
E/onPageScrolled: 0, 0.0013888889, 1
E/onPageScrolled: 0, 0.0, 0
结论:
当ViewPager页面值为0(第一页)且当参数一为0时,页面的状态时从 第一页到第二页
当ViewPager页面值为1(第二页)且当参数一为0时,页面的状态时从 第二页到第一页
position记录的是滑动区间较小的索引,positionOffset记录的是滑动的百分小数;向右滑,positionOffset从0-1,向左滑,positionOffset 从1到0.
下面直接贴代码了
/**
* @author crazyZhangxl on 2018-11-2 9:49:55.
* Describe: 使用布局文件 RadioGroup+底部指示器 实现指示器
*/
public class IndicatorDemo1Activity extends AppCompatActivity {
private ViewPager mViewPager;
private RadioGroup mRadioMain;
private RadioButton mRadioAc1,mRadioAc2,mRadioAc3;
private ImageView mIvLinear;
private int lineWidth = 80;
private int currentPage = -1;
private int screenWidth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_indicator_demo1);
initViews();
initTabLines();
initListener();
}
private void initListener() {
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// 对于三个标签 那么一共只有四种情况
//{1. 0->1 1->2 1->0 2->1}
Log.e("onPageScrolled",position+", "+positionOffset+", "+positionOffsetPixels);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mIvLinear.getLayoutParams();
// (position+positionOffset)*getScreenWidthWidth()/3f 为偏移量; 右边相加的是居中偏移
layoutParams.leftMargin = (int)((position+positionOffset)*getScreenWidthWidth()/3f+getScreenWidthWidth()/6-lineWidth/2);
mIvLinear.setLayoutParams(layoutParams);
}
@Override
public void onPageSelected(int position) {
currentPage = position;
switch (position){
case 0:
mRadioAc1.setChecked(true);
break;
case 1:
mRadioAc2.setChecked(true);
break;
case 2:
mRadioAc3.setChecked(true);
break;
default:
break;
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void initTabLines() {
mRadioMain = findViewById(R.id.rgMain);
mRadioAc1 = findViewById(R.id.rbAc1);
mRadioAc2 = findViewById(R.id.rbAc2);
mRadioAc3 = findViewById(R.id.rbAc3);
mRadioAc1.setChecked(true);
mRadioMain.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.rbAc1:
mRadioAc1.setChecked(true);
mViewPager.setCurrentItem(0);
break;
case R.id.rbAc2:
mRadioAc2.setChecked(true);
mViewPager.setCurrentItem(1);
break;
case R.id.rbAc3:
mRadioAc3.setChecked(true);
mViewPager.setCurrentItem(2);
break;
default:
break;
}
}
});
mIvLinear = findViewById(R.id.imageIndicator);
//*****
// 只有其父布局为LinearLatout时,才能够有 LinearLayoutParams,才能够有leftMargin 设置左边间距
//****