Android自定义控件之仿京东商城下拉刷新
来源:互联网 发布:中国网络好声音苏棱然 编辑:程序博客网 时间:2024/04/29 00:56
前面写了4篇自定义控件的博客,并且开通了一个专栏,把4篇文章添加到专栏中了,耐心等待博客专栏的徽章出现,奈何等了几周后还是没有出现,后来发现至少需要5篇文章才能出现专栏徽章,于是有了这篇仿我大京东快递小哥的下拉刷新。
直接上图先!
分析
这个下拉刷新效果分为两个部分:
step1:快递小哥和快递包裹的缩放效果,看上去就像是快递小哥跑过来一手拿过快递的样子
step2:快递小哥拿到包裹后,开启暴走模式!玩命送快递
PS:不得不赞一下京东的快递,真的很快!
step1
好了马屁拍完了,我们先来看一看第一部分的效果是怎么实现的。首先快递小哥和包裹是两张图片
我们看到快递小哥是从小变大的,快递包裹也是从小变大的,所以我们要自定义一个控件,就起名为FirstStepView.java吧
public class FirstSetpView extends View{ private Bitmap goods; private Bitmap people; private Bitmap peopleWithGoods; private int measuredWidth; private int measuredHeight; private float mCurrentProgress; private int mCurrentAlpha; private Paint mPaint; private Bitmap scaledPeople; private Bitmap scaledGoods; public FirstSetpView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public FirstSetpView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public FirstSetpView(Context context) { super(context); init(); } private void init(){ //包裹bitmap goods = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_goods_0); //快递小哥bitmap people = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_people_0); //这是后面动画中的最后一张图片,拿这张图片的作用是用它的宽高来测量 //我们这个自定义View的宽高 peopleWithGoods = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_people_3); //来个画笔,我们注意到快递小哥和包裹都有一个渐变效果的,我们用 //mPaint.setAlpha来实现这个渐变的效果 mPaint = new Paint(); //首先设置为完全透明 mPaint.setAlpha(0); } /** * 测量方法 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } //测量宽度 private int measureWidth(int widthMeasureSpec){ int result = 0; int size = MeasureSpec.getSize(widthMeasureSpec); int mode = MeasureSpec.getMode(widthMeasureSpec); if (MeasureSpec.EXACTLY == mode) { result = size; }else { result = peopleWithGoods.getWidth(); if (MeasureSpec.AT_MOST == mode) { result = Math.min(result, size); } } return result; } //测量高度 private int measureHeight(int heightMeasureSpec){ int result = 0; int size = MeasureSpec.getSize(heightMeasureSpec); int mode = MeasureSpec.getMode(heightMeasureSpec); if (MeasureSpec.EXACTLY == mode) { result = size; }else { result = peopleWithGoods.getHeight(); if (MeasureSpec.AT_MOST == mode) { result = Math.min(result, size); } } return result; } //在这里面拿到测量后的宽和高,w就是测量后的宽,h是测量后的高 @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); measuredWidth = w; measuredHeight = h; //根据测量后的宽高来对快递小哥做一个缩放 scaledPeople = Bitmap.createScaledBitmap(people,measuredWidth,measuredHeight,true); //根据测量后的宽高来对快递包裹做一个缩放 scaledGoods = Bitmap.createScaledBitmap(goods, scaledPeople.getWidth()*10/27, scaledPeople.getHeight()/5, true); } /** * 绘制方法 * @param canvas */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //由于包裹和快递小哥要分别来画,所以使用save和restore方法 //save //画包裹 //restore //save //画小哥 //restore canvas.save(); canvas.scale(mCurrentProgress, mCurrentProgress , measuredWidth-scaledGoods.getWidth()/2 , measuredHeight/2); mPaint.setAlpha(mCurrentAlpha); canvas.drawBitmap(scaledGoods, measuredWidth-scaledGoods.getWidth(), measuredHeight/2-scaledGoods.getHeight()/2, mPaint); canvas.restore(); canvas.save(); canvas.scale(mCurrentProgress, mCurrentProgress , 0 , measuredHeight/2); mPaint.setAlpha(mCurrentAlpha); canvas.drawBitmap(scaledPeople, 0,0,mPaint); canvas.restore(); } /** * 根据进度来对小哥和包裹进行缩放 * @param currentProgress */ public void setCurrentProgress(float currentProgress){ this.mCurrentProgress = currentProgress; this.mCurrentAlpha = (int) (currentProgress*255); }}
还是老方法,我们写完这个效果,就先来测试一下吧,还是用一个SeekBar来模拟一个进度值
public class MainActivity extends Activity { private SeekBar sb; private FirstSetpView mFirstStepView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sb = (SeekBar) findViewById(R.id.seekbar); mFirstStepView = (FirstSetpView) findViewById(R.id.firstview); sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onStopTrackingTouch(SeekBar seekBar) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float currentProgress = (float)seekBar.getProgress()/(float)seekBar.getMax(); mFirstStepView.setCurrentProgress(currentProgress); mFirstStepView.invalidate(); } }); }}
step2
好了,第一部分已经完成,接下来我们看第二部分,第二部分就简单了,就是一个帧动画,一共有3张图片
我们在res/drawable/下写一个帧动画second_step_animation.xml
<?xml version="1.0" encoding="utf-8"?><animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false" > <item android:drawable="@mipmap/app_refresh_people_1" android:duration="70"/> <item android:drawable="@mipmap/app_refresh_people_2" android:duration="70"/> <item android:drawable="@mipmap/app_refresh_people_3" android:duration="70"/></animation-list>
我们为了保证第一阶段和第二阶段的View的宽和高一致,我们还要再自定义一个View,不过我们不用去画什么,只需要测量一下宽和高,让他和FirstSetpView的宽高保持一致
public class SecondStepView extends View{ private Bitmap endBitmap; public SecondStepView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public SecondStepView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SecondStepView(Context context) { super(context); init(); } private void init(){ //拿到帧动画第三章图片,我们的FirstStepView的宽高也是根据这张图片来测量的,所以我们就能 //保证两个View的宽高一致了 endBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.app_refresh_people_3); } /** * 只需要测量方法 * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } private int measureWidth(int widthMeasureSpec){ int result = 0; int size = MeasureSpec.getSize(widthMeasureSpec); int mode = MeasureSpec.getMode(widthMeasureSpec); if (MeasureSpec.EXACTLY == mode) { result = size; }else { result = endBitmap.getWidth(); if (MeasureSpec.AT_MOST == mode) { result = Math.min(size, result); } } return result; } private int measureHeight(int heightMeasureSpec){ int result = 0; int size = MeasureSpec.getSize(heightMeasureSpec); int mode = MeasureSpec.getMode(heightMeasureSpec); if (MeasureSpec.EXACTLY == mode) { result = size; }else { result = endBitmap.getHeight(); if (MeasureSpec.AT_MOST == mode) { result = Math.min(size, result); } } return result; }}
好了,接下来再复习一下怎么执行帧动画
secondStepView = (SecondStepView) headerView.findViewById(R.id.second_step_view); secondStepView.setBackgroundResource(R.drawable.second_step_animation); secondAnimation = (AnimationDrawable) secondStepView.getBackground();
secondAnimation是一个AnimationDrawable对象,我们可以调用他的start方法开始帧动画,调用stop结束帧动画
secondAnimation.start();secondAnimation.stop();
下拉刷新的实现
这一部分我在Android自定义控件之仿美团下拉刷新 中已经详细分析过了,所以这里就不再赘述了。
完整代码
欢迎大家上我的GitHub上下载源码
- Android自定义控件之仿京东商城下拉刷新
- Android自定义控件之仿京东商城下拉刷新
- Android自定义控件之仿汽车之家下拉刷新
- Android自定义控件之仿汽车之家下拉刷新
- Android自定义控件之仿美团下拉刷新
- Android自定义控件之仿美团下拉刷新
- Android自定义控件之仿美团下拉刷新
- Android自定义之高仿淘宝下拉刷新
- Android自定义控件下拉刷新
- 自定义下拉刷新之仿AcFun下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android自定义下拉刷新动画--仿百度外卖下拉刷新
- Android开发之自定义控件--ListView的下拉刷新功能
- Android自定义控件实战——实现仿IOS下拉刷新上拉加载 PullToRefreshLayout
- 互联网技术发展之路(7)- 网络层技术剖析
- Same Tree
- coderforce 500B New Year Permutation
- 二次剩余
- 扫盲:SSH中 struts hibernate spring 各起什么作用
- Android自定义控件之仿京东商城下拉刷新
- ViewPager 详解(一)---基本入门
- Install Multi Neo4J-server on Single Server
- 数据结构实验之图论二:基于邻接表的广度优先搜索遍历
- kmp算法的实现及心得体会
- UVA 题目401 - Palindromes(回文词)
- 开博笔记
- Git -- 不错的指导
- MATLAB的cat()函数