利用 viewPager ,ShapeDrawable 实现带小圆球的页面滑动
来源:互联网 发布:wamp php.ini在哪 编辑:程序博客网 时间:2024/04/25 13:50
首先声明文章的代码来自于 github 上的开源库,但是因为下载后的时间较长。没有去逐个寻找其出处。文中修改了小许的代码,并为文中的代码加了些个人的理解(注释)。具体的如代码中所示。关于 ShapeDrawable 可以参见上一篇博客:http://blog.csdn.net/antimage08/article/details/50373159。
运行效果如下(其背景颜色是随机生成的):
定义属性资源 attrs.xml :
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="CircleDisplay"> <attr name="ci_radius" format="dimension"/> <attr name="ci_margin" format="dimension"/> <attr name="ci_background" format="color|integer"/> <attr name="ci_selected_background" format="color|integer"/> <attr name="ci_gravity"> <enum name="left" value="0"/> <enum name="center" value="1"/> <enum name="right" value="2"/> </attr> <attr name="ci_mode"> <enum name="inside" value="0"/> <enum name="outside" value="1"/> <enum name="solo" value="2"/> </attr> </declare-styleable></resources>
content_main.xml :
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:CircleDisplay="http://schemas.android.com/apk/com.crazy.circle" 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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.crazy.circle.MainActivity" tools:showIn="@layout/activity_main"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" /> <com.crazy.circledisplay.CircleDisplay android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="40dp" android:layout_centerVertical="true" CircleDisplay:ci_background="@android:color/white" CircleDisplay:ci_gravity="center" CircleDisplay:ci_margin="5dp" CircleDisplay:ci_mode="outside" CircleDisplay:ci_radius="10dp" CircleDisplay:ci_selected_background="0xffe6454a" /></RelativeLayout>
ShapeHolder.java :
package com.crazy.circledisplay;import android.graphics.Paint;import android.graphics.drawable.ShapeDrawable;import android.graphics.drawable.shapes.Shape;/** * 该类主要用于提供二维图形(包含多个小圆球,提供给 CircleDisplay 类) */public class ShapeHolder { private float x = 0, y = 0; private ShapeDrawable shape; private int color; private float alpha = 1f; private Paint paint; public ShapeHolder(ShapeDrawable s) { shape = s; } public void setPaint(Paint value) { paint = value; } public Paint getPaint() { return paint; } public void setX(float value) { x = value; } public float getX() { return x; } public void setY(float value) { y = value; } public float getY() { return y; } public void setShape(ShapeDrawable value) { shape = value; } public ShapeDrawable getShape() { return shape; } public int getColor() { return color; } public void setColor(int value) { shape.getPaint().setColor(value); color = value; } public void setAlpha(float alpha) { this.alpha = alpha; shape.setAlpha((int) ((alpha * 255f) + .5f)); } public float getWidth() { return shape.getShape().getWidth(); } public void setWidth(float width) { Shape s = shape.getShape(); s.resize(width, s.getHeight()); } public float getHeight() { return shape.getShape().getHeight(); } public void setHeight(float height) { Shape s = shape.getShape(); s.resize(s.getWidth(), height); } /** * 其中的 resize( width,height) 必须在 onDraw()之前调用 */ public void resizeShape(final float width,final float height){ shape.getShape().resize(width,height); }}
MainActivity.java :
package com.crazy.circledisplay;import android.os.Bundle;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;import java.util.Random;public class MainActivity extends AppCompatActivity { private List<View> viewList; private ViewPager viewPager; private CircleDisplay circleDisplay; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); initData(); viewPager = (ViewPager) findViewById(R.id.viewpager); viewPager.setAdapter(pagerAdapter); circleDisplay = (CircleDisplay) findViewById(R.id.indicator); // 让小圆球与 ViewPager 关联起来 circleDisplay.setViewPager(viewPager); } /** * 初始化,随机得到背景颜色 */ private void initData(){ viewList = new ArrayList<View>(); Random random = new Random(); for(int i=0;i<5;i++){ View view = new View(this); view.setBackgroundColor(0xff000000| random.nextInt(0x00ffffff)); viewList.add(view); } } // 关于 PagerAdapter 可以参考前文 PagerAdapter pagerAdapter = new PagerAdapter() { @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public int getCount() { return viewList.size(); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(viewList.get(position)); } @Override public int getItemPosition(Object object) { return super.getItemPosition(object); } @Override public CharSequence getPageTitle(int position) { return "title"; } @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(viewList.get(position)); return viewList.get(position); } };}
CircleDisplay.java :
package com.crazy.circledisplay;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.drawable.ShapeDrawable;import android.graphics.drawable.shapes.OvalShape;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.view.View;import java.util.ArrayList;import java.util.List;public class CircleDisplay extends View{ private ViewPager viewPager; // 小圆球的集合 private List<ShapeHolder> tabItems; private ShapeHolder movingItem; // 属性资源 private int mCurItemPosition; private float mCurItemPositionOffset; private float mIndicatorRadius; private float mIndicatorMargin; private int mIndicatorBackground; private int mIndicatorSelectedBackground; private Gravity mIndicatorLayoutGravity; private Mode mIndicatorMode; // 设置园的半径 private final int DEFAULT_RADIUS = 15; // 设置两个圆之间的距离 private final int DEFAULT_MARGIN = 40; // 设置圆的颜色(未选中时) private final int DEFAULT_BACKGROUND = Color.WHITE; // 设置选中时圆的颜色 private final int DEFAULT_SELECTED_BACKGROUND = Color.RED; // 设置显示的位置(水平方向) private final int DEFAULT_LAYOUT_GRAVITY = Gravity.CENTER.ordinal(); private final int DEFAULT_MODE = Mode.SOLO.ordinal(); public enum Gravity{ LEFT, // 左 CENTER, // 中 RIGHT // 右 } public enum Mode{ INSIDE, OUTSIDE, SOLO } public CircleDisplay(Context context) { this(context, null); } public CircleDisplay(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleDisplay(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs); } private void init(Context context,AttributeSet attrs){ tabItems = new ArrayList<>(); handleTypedArray(context, attrs); } /** * 检索从 atrrs.xml 这个结构对应于给定的属性 obtainstyledattributes 的位置 * @param context * @param attrs */ private void handleTypedArray(Context context, AttributeSet attrs) { if(attrs == null) return; TypedArray typedArray = context.obtainStyledAttributes( attrs, R.styleable.CircleDisplay); // 获取半径参数 mIndicatorRadius = typedArray.getDimensionPixelSize( R.styleable.CircleDisplay_ci_radius, DEFAULT_RADIUS); mIndicatorMargin = typedArray.getDimensionPixelSize( R.styleable.CircleDisplay_ci_margin, DEFAULT_MARGIN); mIndicatorBackground = typedArray.getColor( R.styleable.CircleDisplay_ci_background, DEFAULT_BACKGROUND); mIndicatorSelectedBackground = typedArray.getColor( R.styleable.CircleDisplay_ci_selected_background, DEFAULT_SELECTED_BACKGROUND); int gravity = typedArray.getInt( R.styleable.CircleDisplay_ci_gravity, DEFAULT_LAYOUT_GRAVITY); mIndicatorLayoutGravity = Gravity.values()[gravity]; int mode = typedArray.getInt( R.styleable.CircleDisplay_ci_mode, DEFAULT_MODE); mIndicatorMode = Mode.values()[mode]; typedArray.recycle(); } public void setViewPager(final ViewPager viewPager){ this.viewPager = viewPager; createTabItems(); createMovingItem(); setUpListener(); } private void setUpListener() { viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { /** * * @param position 当前的位置,从 0 开始记录; * @param positionOffset 位置偏移量,从右往左滑动,数值越来越大(当翻页后数值变为 0.0), * 但都小于 1.0;从左往右翻动,数值逐渐减小 (当翻页后数值变为 0.0); * @param positionOffsetPixels 位置偏移像素,从右往左滑动,数值越来越大(当翻页后数值变为 0); * 从左往右翻动,数值逐渐减小 (当翻页后数值变为 0) */ @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { super.onPageScrolled(position, positionOffset, positionOffsetPixels); if (mIndicatorMode != Mode.SOLO) { trigger(position, positionOffset); } } @Override public void onPageSelected(int position) { super.onPageSelected(position); // 当滑动到下一张图片时,绘制小圆球 if (mIndicatorMode == Mode.SOLO) { trigger(position, 0); } } }); } /** * 当 ViewPager 的条目改变时,重绘 * @param position * @param positionOffset */ private void trigger(int position,float positionOffset){ CircleDisplay.this.mCurItemPosition = position; CircleDisplay.this.mCurItemPositionOffset = positionOffset; requestLayout(); invalidate(); } private void createTabItems() { for (int i = 0; i < viewPager.getAdapter().getCount(); i++) { OvalShape circle = new OvalShape(); ShapeDrawable drawable = new ShapeDrawable(circle); ShapeHolder shapeHolder = new ShapeHolder(drawable); Paint paint = drawable.getPaint(); paint.setColor(mIndicatorBackground); // 去锯齿 paint.setAntiAlias(true); shapeHolder.setPaint(paint); tabItems.add(shapeHolder); } } private void createMovingItem() { OvalShape circle = new OvalShape(); ShapeDrawable drawable = new ShapeDrawable(circle); movingItem = new ShapeHolder(drawable); Paint paint = drawable.getPaint(); paint.setColor(mIndicatorSelectedBackground); paint.setAntiAlias(true); switch (mIndicatorMode){ case INSIDE: // 设置混合模式 (只在源图像和目标图像相交的地方绘制目标图像) paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); break; case OUTSIDE: paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); break; case SOLO: paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); break; } movingItem.setPaint(paint); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); final int width = getWidth(); final int height = getHeight(); layoutTabItems(width, height); layoutMovingItem(mCurItemPosition, mCurItemPositionOffset); } private void layoutTabItems(final int containerWidth,final int containerHeight){ if(tabItems == null){ throw new IllegalStateException("forget to create tabItems?"); } // y 轴方向上的中点 final float yCoordinate = containerHeight*0.5f; final float startPosition = startDrawPosition(containerWidth); for(int i=0;i<tabItems.size();i++){ ShapeHolder item = tabItems.get(i); item.resizeShape(2* mIndicatorRadius,2* mIndicatorRadius); item.setY(yCoordinate- mIndicatorRadius); float x = startPosition + (mIndicatorMargin + mIndicatorRadius*2)*i; item.setX(x); } } private float startDrawPosition(final int containerWidth){ // 居左时,绘制小圆球起始位置从 0 开始 if(mIndicatorLayoutGravity == Gravity.LEFT) return 0; // 所有小圆球的所占位置的宽度(包括小圆球之间的距离) float tabItemsLength = tabItems.size()*(2* mIndicatorRadius + mIndicatorMargin)- mIndicatorMargin; // 超出屏幕宽度时,起始位置为 0 if(containerWidth<tabItemsLength){ return 0; } // 小圆球整体居中时 if(mIndicatorLayoutGravity == Gravity.CENTER){ // 小圆球到屏幕边缘的最短距离 return (containerWidth-tabItemsLength)/2; } return containerWidth - tabItemsLength; } private void layoutMovingItem(final int position,final float positionOffset){ if(movingItem == null){ throw new IllegalStateException("forget to create movingItem?"); } ShapeHolder item = tabItems.get(position); movingItem.resizeShape(item.getWidth(), item.getHeight()); float x = item.getX()+(mIndicatorMargin + mIndicatorRadius*2)*positionOffset; movingItem.setX(x); movingItem.setY(item.getY()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // saveLayer 可以看成有多个图层(Layer),缺省情况可以看作是只有一个图层Layer. // 如果需要按层次来绘图,Android的Canvas可以使用SaveLayerXXX, Restore 来创建一些中间层, // 对于这些Layer是按照“栈结构“来管理的。也就是可以通过修改其属性,能够看到看到底层的视图 int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); for(ShapeHolder item : tabItems){ canvas.save(); canvas.translate(item.getX(),item.getY()); item.getShape().draw(canvas); canvas.restore(); } if(movingItem != null){ canvas.save(); canvas.translate(movingItem.getX(), movingItem.getY()); movingItem.getShape().draw(canvas); canvas.restore(); } canvas.restoreToCount(sc); } // 注释掉的这部分可以扩展其他的功能/* public void setIndicatorRadius(float mIndicatorRadius) { this.mIndicatorRadius = mIndicatorRadius; } public void setIndicatorMargin(float mIndicatorMargin) { this.mIndicatorMargin = mIndicatorMargin; } public void setIndicatorBackground(int mIndicatorBackground) { this.mIndicatorBackground = mIndicatorBackground; } public void setIndicatorSelectedBackground(int mIndicatorSelectedBackground) { this.mIndicatorSelectedBackground = mIndicatorSelectedBackground; } public void setIndicatorLayoutGravity(Gravity mIndicatorLayoutGravity) { this.mIndicatorLayoutGravity = mIndicatorLayoutGravity; } public void setIndicatorMode(Mode mIndicatorMode) { this.mIndicatorMode = mIndicatorMode; }*/}
0 0
- 利用 viewPager ,ShapeDrawable 实现带小圆球的页面滑动
- 利用 ViewPager 等,实现带小圆球的图片滑动,并且只有第一次安装app时才出现欢迎界面(图片)
- 利用viewpager实现页面的滑动切换
- ViewPager实现TabHost动态添加、删除Fragment,用红色小圆球指示当前页面
- ViewPager实现滑动页面
- ViewPager(一):带指示图标的滑动页面的实现
- Android之自定义View实现随手势滑动的小圆球
- 督促学习——ViewPager实现滑动的Preference页面且带ActionBar
- ViewPager+Fragment 实现滑动页面的效果
- Viewpager+Fragment实现页面的滑动
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- 利用viewpager、Fragment、pagertabStrip 实现多页面滑动效果
- 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、PagerTabStrip实现多页面滑动效果
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效
- Android 利用ViewPager、Fragment、PagerTabStrip实现多页面滑动效
- mysql自定义函数
- uva 11012——Cosmic Cabbages
- git设置push
- NGUI 这一年填过的那些坑(不定时更新)
- 4道java小题
- 利用 viewPager ,ShapeDrawable 实现带小圆球的页面滑动
- JavaScript window对象 - 浏览器对象模型(BOM),使 JavaScript 有能力与浏览器“对话”
- MySQL存储过程详解
- iOS9模拟器无法进行 HTTP 网络请求的解决方案
- ubuntu下tftp安装、配置、
- Redhat 6.5 安装Oracle11g
- C语言总结
- description方法到底什么时候会被调用以及description用法
- Gitlab与ssh登陆的恩怨情仇