您的位置:首页 > 移动开发 > Android开发

Android自定义控件之仿美团下拉刷新

2016-02-24 16:04 495 查看


美团的下拉刷新分为三个状态:

第一个状态为下拉刷新状态(pull to refresh),在这个状态下是一个绿色的椭圆随着下拉的距离动态改变其大小。

第二个部分为放开刷新状态(release to refresh),在这个状态下是一个帧动画,效果为从躺着变为站起来的动画。

第三个部分为刷新状态(refreshing),在这个状态下也是一个帧动画,是摇头的动画。

其中第二和第三个状态很简单,就是两个帧动画,第一个状态我们可以用自定义View来实现。

第一个状态的实现:

我们的思路是:当前这个椭圆形有一个进度值,这个进度值从0变为1,然后对这个椭圆形进行缩放,我们可以使用自定义View来实现这个效果,我们先来用一个SeekBar来模仿一下下拉距离的进度



我们解压美团apk后拿到这张图片:



public class MeiTuanRefreshFirstStepView extends View{

private Bitmap initialBitmap;
private int measuredWidth;
private int measuredHeight;
private Bitmap endBitmap;
private float mCurrentProgress;
private Bitmap scaledBitmap;

public MeiTuanRefreshFirstStepView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init(context);
}

public MeiTuanRefreshFirstStepView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

public MeiTuanRefreshFirstStepView(Context context) {
super(context);
init(context);
}

private void init(Context context) {
//这个就是那个椭圆形图片
initialBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.pull_image));
//这个是第二个状态娃娃的图片,之所以要这张图片,是因为第二个状态和第三个状态的图片的大小是一致的,而第一阶段
//椭圆形图片的大小与第二阶段和第三阶段不一致,因此我们需要根据这张图片来决定第一张图片的宽高,来保证
//第一阶段和第二、三阶段的View的宽高一致
endBitmap = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.pull_end_image_frame_05));
}

/**
* 重写onMeasure方法主要是设置wrap_content时 View的大小
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//根据设置的宽度来计算高度  设置为符合第二阶段娃娃图片的宽高比例
setMeasuredDimension(measureWidth(widthMeasureSpec),measureWidth(widthMeasureSpec)*endBitmap.getHeight()/endBitmap.getWidth());
}

/**
* 当wrap_content的时候,宽度即为第二阶段娃娃图片的宽度
* @param widMeasureSpec
* @return
*/
private int measureWidth(int widMeasureSpec){
int result = 0;
int size = MeasureSpec.getSize(widMeasureSpec);
int mode = MeasureSpec.getMode(widMeasureSpec);
if (mode == MeasureSpec.EXACTLY){
result = size;
}else{
result = endBitmap.getWidth();
if (mode == MeasureSpec.AT_MOST){
result = Math.min(result,size);
}
}
return result;
}

/**
* 在onLayout里面获得测量后View的宽高
* @param changed
* @param left
* @param top
* @param right
* @param bottom
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
measuredWidth = getMeasuredWidth();
measuredHeight = getMeasuredHeight();
//根据第二阶段娃娃宽高  给椭圆形图片进行等比例的缩放
scaledBitmap = Bitmap.createScaledBitmap(initialBitmap, measuredWidth,measuredWidth*initialBitmap.getHeight()/initialBitmap.getWidth(), true);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//这个方法是对画布进行缩放,从而达到椭圆形图片的缩放,第一个参数为宽度缩放比例,第二个参数为高度缩放比例,
canvas.scale(mCurrentProgress, mCurrentProgress, measuredWidth/2, measuredHeight/2);
//将等比例缩放后的椭圆形画在画布上面
canvas.drawBitmap(scaledBitmap,0,measuredHeight/4,null);

}

/**
* 设置缩放比例,从0到1  0为最小 1为最大
* @param currentProgress
*/
public void setCurrentProgress(float currentProgress){
mCurrentProgress = currentProgress;
}


然后在Activity里面:

第二个状态的实现:

第二个状态是一个帧动画,我们为了保证View大小的统一,我们也进行自定义View,这个自定义View很简单,只是为了和第一阶段View的宽高保证一致即可

我们用xml定义一组帧动画

帧动画的启动和停止方式:

第三个状态的实现:

和第二个状态同理,我们也通过自定义View来确保三个状态的View的宽高保持一致

?
我们在xml中定义一组帧动画:

帧动画的启动和停止方式和第二个状态的一样

下拉刷新的实现:

首先我们要定义好几个状态,下拉刷新有这样几个状态:

DONE:隐藏的状态

PULL_TO_REFRESH:下拉刷新的状态

RELEASE_TO_REFRESH:松开刷新的状态

REFRESHING:正在刷新的状态

一切准备就绪,在Activity中使用:

public class MainActivity extends Activity implements OnMeiTuanRefreshListener{
private MeiTuanListView mListView;
private List<string> mDatas;
private ArrayAdapter<string> mAdapter;
private final static int REFRESH_COMPLETE = 0;
/**
* mHandler运行在主线程,因为setOnRefreshComplete需要改变ui,必须在主线程去改变ui
* 所以在handleMessage中调用mListView.setOnRefreshComplete();
*/
private Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case REFRESH_COMPLETE:
mListView.setOnRefreshComplete();
mAdapter.notifyDataSetChanged();
mListView.setSelection(0);
break;

default:
break;
}
};
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (MeiTuanListView) findViewById(R.id.listview);
String[] data = new String[]{hello world,hello world,hello world,hello world,
hello world,hello world,hello world,hello world,hello world,
hello world,hello world,hello world,hello world,hello world,};
mDatas = new ArrayList<string>(Arrays.asList(data));
mAdapter = new ArrayAdapter<string>(this, android.R.layout.simple_list_item_1,mDatas);
mListView.setAdapter(mAdapter);
mListView.setOnMeiTuanRefreshListener(this);
}

@Override
public void onRefresh() {
new Thread(new Runnable() {

@Override
public void run() {
try {
Thread.sleep(3000);
mDatas.add(0, new data);
mHandler.sendEmptyMessage(REFRESH_COMPLETE);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: