Android 微信刷新&自带下拉刷新SwipeRefreshLayout(二)
来源:互联网 发布:三菱plc模块化编程 编辑:程序博客网 时间:2024/06/07 09:52
上一篇博客Android 微信刷新&自带下拉刷新SwipeRefreshLayout(一)我们简单介绍了一下SwipeRefreshLayout的原理,以及对它对了一个简单的拆分,这一节我们就来改改它的代码,实现一个微信朋友圈的下拉刷新效果:
我们先看一下系统自带的下拉刷新效果:
下拉刷新的时候,可以看到一个进度条view,这个进度条view上一篇博客有抽取出来的,不懂的小伙伴可以去看看哈。
下面说一下仿微信下拉刷新的思路:
1、修改进度条view为我们微信的图片,然后给个动画让图片旋转
2、修改微信图片出现的位置为左上角
3、当下拉的时候,header图片跟着下拉
好啦!明确了我们的目的之后,我们就开始修改代码了。
首先替换掉默认的加载进度条为微信的图片
我们看到MaterialProgressDrawable这个类,这个就是进度条view(上一节也有提到),我们仿照着自己写一个叫WechatProgressDrawable的,代码比较简单,我就直接上代码了:
package com.yasin.eledemo.swipe;import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.ColorFilter;import android.graphics.PixelFormat;import android.graphics.Rect;import android.graphics.drawable.Animatable;import android.graphics.drawable.Drawable;import android.util.DisplayMetrics;import android.view.View;import android.view.animation.Animation;import android.view.animation.LinearInterpolator;import android.view.animation.Transformation;import com.yasin.eledemo.R;import java.util.ArrayList;/** * Created by leo on 17/5/14. */public class WechatProgressDrawable extends Drawable implements Animatable{ private Resources mResources; private View mParent; /** The list of animators operating on this drawable. */ private final ArrayList<Animation> mAnimators = new ArrayList<Animation>(); // Maps to ProgressBar.Large style static final int LARGE = 0; // Maps to ProgressBar default style static final int DEFAULT = 1; static final float LARGE_SIZE=56; static final float DEFAULT_SIZE=40; static final long ANIMATION_DURATION=3000; private float mWidth,mHeight; private Animation mAnimation; private float mRotation; private Bitmap mBitmap; public WechatProgressDrawable(Context context, View parent) { mParent = parent; mResources = context.getResources(); updateSizes(DEFAULT); setupAnimators(); mBitmap= BitmapFactory.decodeResource(mResources, R.mipmap.icon_wechat1); mBitmap=Bitmap.createScaledBitmap(mBitmap,(int)mWidth,(int)mHeight,true); } public void updateSizes( int size) { if (size == LARGE) { setSizeParameters(LARGE_SIZE, LARGE_SIZE); } else { setSizeParameters(DEFAULT_SIZE, DEFAULT_SIZE); } } private void setSizeParameters(float width, float height) { final DisplayMetrics metrics = mResources.getDisplayMetrics(); final float screenDensity = metrics.density; mWidth = width * screenDensity; mHeight = height * screenDensity; } private void setupAnimators() { final Animation animation = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { float rotation=interpolatedTime*1.0f*360; setProgressRotation(rotation); } }; animation.setRepeatCount(Animation.INFINITE); animation.setRepeatMode(Animation.RESTART); animation.setInterpolator(new LinearInterpolator()); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { // do nothing } @Override public void onAnimationRepeat(Animation animation) { } }); mAnimation = animation; } @Override public void draw(Canvas c) { final Rect bounds = getBounds(); final int saveCount = c.save(); c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY()); c.drawBitmap(mBitmap,0,0,null); c.restoreToCount(saveCount); } /** * Set the amount of rotation to apply to the progress spinner. * * @param rotation Rotation is from [0..1] */ public void setProgressRotation(float rotation) { this.mRotation=rotation; invalidateSelf(); } @Override public int getIntrinsicHeight() { return (int) mHeight; } @Override public int getIntrinsicWidth() { return (int) mWidth; } @Override public void setAlpha(int alpha) { } public int getAlpha() { return 255; } @Override public void setColorFilter(ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public void start() { mAnimation.reset(); mAnimation.setDuration(ANIMATION_DURATION); mParent.startAnimation(mAnimation); } @Override public void stop() { mParent.clearAnimation(); setProgressRotation(0); } @Override public boolean isRunning() { final ArrayList<Animation> animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animation animator = animators.get(i); if (animator.hasStarted() && !animator.hasEnded()) { return true; } } return false; }}
我们来测试一下:
WechatProgressDrawable drawable1=new WechatProgressDrawable(this,container); drawable1.setAlpha(255); mShopCart.setImageDrawable(drawable1); drawable1.start();
卧槽,gif录制出问题了还是咋了,调用了start方法后中间那个图标是可以旋转的。
好啦!!搞定基础的进度条view后,我们需要把它放进SwipeRefreshLayout中,在SwipeRefreshLayout中我们看到原有的
int mSpinnerOffsetEnd; MaterialProgressDrawable mProgress; private Animation mScaleAnimation;
我们把 MaterialProgressDrawable对象改为
int mSpinnerOffsetEnd; WechatProgressDrawable mProgress; private Animation mScaleAnimation;
好啦!可能有些地方的api你得删掉了,比如说原有的:
mProgress.showArrow(true);
这些方法你就得删掉了,因为我们的WechatProgressDrawable里面没有这些方法,并且我们也不需要了。
接下来我们需要把WechatProgressDrawable摆放在左上角的位置,让它可以从左上角出来:
在SwipeRefreshLayout的onlayout方法中处理了,
原来是这样的:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final int width = getMeasuredWidth(); final int height = getMeasuredHeight(); if (getChildCount() == 0) { return; } if (mTarget == null) { ensureTarget(); } if (mTarget == null) { return; } final View child = mTarget; final int childLeft = getPaddingLeft(); final int childTop = getPaddingTop(); final int childWidth = width - getPaddingLeft() - getPaddingRight(); final int childHeight = height - getPaddingTop() - getPaddingBottom(); child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); int circleWidth = mCircleView.getMeasuredWidth(); int circleHeight = mCircleView.getMeasuredHeight(); mCircleView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop, (width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight); }
改过之后:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final int width = getMeasuredWidth(); final int height = getMeasuredHeight(); if (getChildCount() == 0) { return; } if (mTarget == null) { ensureTarget(); } if (mTarget == null) { return; } final View child = mTarget; final int childLeft = getPaddingLeft(); final int childTop = getPaddingTop(); final int childWidth = width - getPaddingLeft() - getPaddingRight(); final int childHeight = height - getPaddingTop() - getPaddingBottom(); child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); int circleWidth = mCircleView.getMeasuredWidth(); int circleHeight = mCircleView.getMeasuredHeight(); mCircleView.layout((100), mCurrentTargetOffsetTop, (100+circleWidth), mCurrentTargetOffsetTop + circleHeight); }
我就简单的设置其在左边100px的位置了。
好吧~修改完毕后,我们就可以简单的玩起来了~~~我就不测试了哈!!
为了和微信几乎一样的效果,我们需要加一个header(也就是一个imageview),然后当往下拉的时候,图片也往下拉,手指松开后,图片回到原有的位置。
其实呢也很简单,
<?xml version="1.0" encoding="utf-8"?><com.yasin.eledemo.swipe.SwipeRefreshLayout 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:id="@+id/id_swipe" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ImageView android:id="@+id/id_head_view" android:layout_width="match_parent" android:layout_height="200dp" android:src="@mipmap/icon_beaty" android:layout_marginTop="-40dp" android:scaleType="centerCrop" />
可以看到,我在LinearLayout下面直接放了一个imageview,然后设置其默认样式为 android:layout_marginTop=”-40dp。
然后我们通过下拉的距离改变android:layout_marginTop来实现我们的图片滑动操作。
首先我们需要在SwipeRefreshLayout中定义一个view叫:
private View mHeadView;
然后我们需要找到我们的 private View mHeadView;并且记录一下原始的margintop值:
private float originHeaderMargin; private void ensureTarget() { // Don't bother getting the parent height if the parent hasn't been laid // out yet. if (mTarget == null) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (!child.equals(mCircleView)) { mTarget = child; break; } } } if(mTarget!=null){ mHeadView=mTarget.findViewById(R.id.id_head_view); if(mHeadView!=null&&mHeadView.getLayoutParams() instanceof MarginLayoutParams){ MarginLayoutParams lp= (MarginLayoutParams) mHeadView.getLayoutParams(); if(originHeaderMargin==0){ originHeaderMargin=lp.topMargin; headerBackAni.setDuration(ANIMATE_TO_START_DURATION); headerBackAni.setInterpolator(new AccelerateDecelerateInterpolator()); } } } }
ensureTarget这个方法我上一节有介绍哈~~
然后当手指滑动的时候,我们需要根据滑动的距离改变margintop值,所以我们进入到SwipeRefreshLayout的onTouchEvent方法中修改下逻辑:
@Overridecase MotionEvent.ACTION_MOVE: { pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex < 0) { Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id."); return false; } final float y = ev.getY(pointerIndex); startDragging(y); if (mIsBeingDragged) { final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE; if (overscrollTop > 0) { Log.e("TAG", "onTouchEvent:--->"+overscrollTop); moveSpinner(overscrollTop); //开始控制headerview的滑动位置 setHeaderMarginOffset(overscrollTop/2); } else { return false; } } break; }
private void setHeaderMarginOffset(float offset) { if (mHeadView != null && mHeadView.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams lp = (MarginLayoutParams) mHeadView.getLayoutParams(); lp.topMargin = (int) (originHeaderMargin+offset); mHeadView.setLayoutParams(lp); } }
然后手指放开的时候,动画回到原来的位置:
case MotionEvent.ACTION_UP: { pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex < 0) { Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id."); return false; } if (mIsBeingDragged) { final float y = ev.getY(pointerIndex); final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE; mIsBeingDragged = false; finishSpinner(overscrollTop); //header动画回到原来的位置 startHeaderBackAni(); mProgress.stop(); } mActivePointerId = INVALID_POINTER; return false; }
} private void startHeaderBackAni(){ mHeadView.clearAnimation(); mHeadView.startAnimation(headerBackAni); }
private final Animation headerBackAni=new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if (mHeadView != null && mHeadView.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams lp = (MarginLayoutParams) mHeadView.getLayoutParams(); float startMargin=lp.topMargin; float endMargin= originHeaderMargin; float margin=startMargin+(endMargin-startMargin)*interpolatedTime; lp.topMargin= (int) margin; mHeadView.setLayoutParams(lp); } } };
好啦!!我们的改造就撸完了~~
附上项目的github链接:
https://github.com/913453448/EleDemo
欢迎入群~~~~
- Android 微信刷新&自带下拉刷新SwipeRefreshLayout(二)
- Android 微信刷新&自带下拉刷新SwipeRefreshLayout(一)
- Android 自带下拉刷新SwipeRefreshLayout
- SwipeRefreshLayout Android 系统自带下拉刷新控件!
- Android篇-系统自带下拉刷新(SwipeRefreshLayout )
- 安卓自带下拉刷新SwipeRefreshLayout
- Google自带下拉刷新组件SwipeRefreshLayout
- android自带下拉刷新
- Android自带下拉刷新SwipeRefreshLayout用法及添加上拉加载功能
- android官方自带下拉刷新功能
- 安卓自带下拉刷新SwipeRefreshLayout添加上拉刷新功能
- 安卓自带下拉刷新SwipeRefreshLayout添加上拉刷新功能
- 安卓自带下拉刷新SwipeRefreshLayout添加上拉刷新功能
- android系统自带下拉刷新控件的实现
- android系统自带下拉刷新控件的实现
- iOS UITableView系统自带下拉刷新
- SwipeRefreshLayout_ 系统自带下拉刷新
- 【LOL盒子】->iOS自带下拉刷新控件【UIRefreshControl】
- 使用Thinkphp框架开发移动端接口(1)
- 关于拼接SQL语句sqlMap的使用方法
- iOS第三方库-CocoaLumberjack-DDLog 使用
- 自学h5 来看看项目常见问题汇总及解决方案
- 企业支付宝账号开发接口教程--JAVA-UTF-8
- Android 微信刷新&自带下拉刷新SwipeRefreshLayout(二)
- java中IO流详解
- 操作系统处理机管理功能(2)之进程通信及调度
- linux之bound网络配置
- 笔记
- 基于Arduino超声波测距/盲区检测
- LabVIEW调用WebService实现天气预报
- 剑指offer 旋转数组的最小数字
- 堆排序