返回
轮播“粘”指器——Android自定义View实战
Android
2023-12-27 15:02:04
轮播在当下App设计中已俨然成为标配,与其相伴而生、代表轮播进度的轮播指示器也亟待灵动美观的设计。尽管市面上现有的指示器样式多采用小圆点形式,且相关实现教程也数不胜数,但仅凭这些还不能满足我们对个性化、功能丰富应用的需求。
本文将引入一个集美观与动感为一身的“粘性”轮播指示器,以期能为有兴趣的读者提供更多参考与启发。
想要自定义Android中的View,首要了解View的核心概念及其绘制流程。View即一种应用层抽象类,它将窗口中所要显示的图像呈现出来。欲了解具体流程,我们需剖析View源码中的底层机制:
- 通过onMeasure()确定View的尺寸。
- 利用onSizeChanged()设置View的实际尺寸。
- 在onDraw()方法中绘制View。
有了理论认识后,不妨先来看看“粘性”指示器的布局样貌:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="200dp" />
<com.zys.stickyindicator.StickyIndicator
android:id="@+id/sticky_indicator"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_below="@+id/viewPager" />
</RelativeLayout>
其中的StickyIndicator即为我们定义的自定义指示器。先别着急着手实现,不妨动动脑筋思考一下:要想黏合的效果得以实现,我们势必需要连续获取轮播图的变化,并在指示器中持续更新其状态,该如何实现?
其实很简单,我们只要监听ViewPager页面滑动时触发的OnPageChangeListener,根据回调方法传递的信息,更新自定义指示器的状态即可。
public class StickyIndicator extends View implements ViewPager.OnPageChangeListener {
private Paint mPaint;
private List<PositionData> mPositionDataList;
private int mWidth;
private int mHeight;
private int mRadius;
private int mSelectPosition;
public StickyIndicator(Context context) {
this(context, null);
}
public StickyIndicator(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public StickyIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setAntiAlias(true);
mPositionDataList = new ArrayList<>();
mRadius = 15;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawCircle(canvas);
drawCurrentCircle(canvas);
}
private void drawCircle(Canvas canvas) {
for (int i = 0; i < mPositionDataList.size(); i++) {
canvas.drawCircle(mPositionDataList.get(i).x, mPositionDataList.get(i).y, mRadius, mPaint);
}
}
private void drawCurrentCircle(Canvas canvas) {
if (mSelectPosition < mPositionDataList.size()) {
mPaint.setColor(Color.BLUE);
canvas.drawCircle(mPositionDataList.get(mSelectPosition).x, mPositionDataList.get(mSelectPosition).y, mRadius, mPaint);
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
float offset = 0;
if (positionOffset != 0) {
offset = (positionOffsetPixels - mWidth / mPositionDataList.size()) / positionOffset;
}
// 计算移动距离
float moveDistance = (mPositionDataList.get(position + 1).x - mPositionDataList.get(position).x) * positionOffset + offset;
mSelectPosition = position;
invalidate();
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
public void setViewPager(ViewPager viewPager) {
viewPager.addOnPageChangeListener(this);
initPositionData(viewPager);
invalidate();
}
private void initPositionData(ViewPager viewPager) {
mPositionDataList.clear();
for (int i = 0; i < viewPager.getAdapter().getCount(); i++) {
PositionData positionData = new PositionData();
positionData.x = mWidth / viewPager.getAdapter().getCount() * i + mWidth / viewPager.getAdapter().getCount() / 2;
positionData.y = mHeight / 2;
mPositionDataList.add(positionData);
}
}
private static class PositionData {
int x;
int y;
}
}
如何使指示器的圆形能够黏合变化?看似困难的问题,实则只要利用回调方法不断地从ViewPager获取页面滑动状态,从而更新指示器的位置数据便可实现。下面几个步骤是实现的关键:
- 通过OnPageChangeListener监听ViewPager的状态。
- 滑动时,根据回调方法传递的信息计算要移动的距离。
- 将计算出的距离应用于指示器的x坐标。
- 更新指示器的x坐标,使之与ViewPager中的当前页面保持一致。
将指示器黏合到指定ViewPager的操作方法也很简单:
stickyIndicator.setViewPager(viewPager);
这样做能够将指示器与ViewPager关联起来,并确保指示器的位置能够根据ViewPager中的页面滑动而相应变化。
有了这些理论准备,现在就可以着手实现这个粘性轮播指示器了。快去试试吧!
轮播指示器是App中常见的一种控件,而“粘性”轮播指示器的实现并不复杂,掌握好本文的技巧,相信你一定能够做出美观又实用的效果。