标签:
转载请注明出处:http://blog.csdn.net/wangpengfei_p/article/details/51420422
昨天想要实现一个下拉刷新的效果,本来想应该比较简单,因为之前在慕课网看见过类似的实现,记得是在listView里面添加footView或是添加headView,监听手指的点击滑动事件来控制view的显示或是隐藏,但是自己按照上面的代码来实现之后发现。这样做有一点不好的地方:
它判断是否刷新的依据是判断listView是不是滑动到了最后一个item。只要一旦滑动到最后一列就会自动刷新数据,我们并不能控制对数据刷新的取消。
于是我想到的QQ主界面的刷新功能:先看看QQ界面的刷新功能:
于是我就想实现这样的一个功能,但在网上搜索了好久大多的都是通过上述方式来实现的。还有一种方式是通过自定义ViewGroup来实现的(原谅我是新手,对于自定义view虽然接触了一些,仍然用不起来T_T)
今天回忆起郭霖大神的那篇博客Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现想了一下我是不是也可以通过动态设置view的margin来对上面的刷新条进行设置呢?
于是今天下午不断测试,不断调试,最终觉得效果有点类似QQ那样的效果了,就迫不及待的要写一篇博客了(≧▽≦)
毕竟第一篇博客,又是新手有什么不对的地方还望大家指正
先来看看最终的效果:
基本上和QQ的刷新很相似了,只是界面好篓的感觉。。。
对了里面有一些代码是借鉴自郭神的博文Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现有兴趣的可以看看这篇博文
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/load_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#FFFFFF"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:id="@+id/main_activity_top_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@drawable/esc"
android:src="@drawable/esc" />
<TextView
android:id="@+id/main_activity_top_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textSize="12sp" />
</LinearLayout>
<ListView
android:id="@+id/main_activity_lv"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
</ListView>
</LinearLayout>
这个布局文件里面主要有两部分,一个是上面的LinearLayout,用于表示刷新界面的布局,里面有一个ImageView和一个TextView用于表示刷新布局的前面的图标和后面的提示文字。这里我们让它们居中显示,因为在后面我们要对这三个控件都要进行操作,所以都要给它们加上id。
还有里面的图片是我从qq的安装包里面随便找的。
另一部分是界面的主体内容部分,我们用一个listView来进行表示,因为listView更有代表性,尤其是在存在有手指滑动的情况下。我们也给它加上id。
好了布局文件我们写好了,之后就是MainActivity里面的逻辑代码了
package com.example.listviewtest;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements OnTouchListener {
// 测试的数据源
private String[] strs = new String[30];
private ListView listView;
private ArrayAdapter<String> adapter;
// 顶部刷新视图的view
private View top;
// 顶部刷新视图中的文字提示
private TextView topTV;
// 顶部刷新试图的图片提示
private ImageView topIv;
// 顶部刷新视图的参数,通过此参数来更改topMargin的值
private LayoutParams topParams;
/**
* 顶部刷新视图的高度,通过topmargin和这个值进行比较来判断刷新视图的状态
*/
private int topHeight = 80;
/**
* 当listview里面的第一个item可见的时候,初始化此值。即当刷新视图出现的时候手指所在的Y轴的位置
*/
private int currentY;
// 这三个值分别是手指按下,移动,和抬起的时候手指的Y轴位置
private int moveY;
private int downY;
private int upY;
/**
* 这个值用于判断
*/
private boolean isRead = false;
/**
* 这个值用于判断当前是不是正在加载数据,如果正在加载,则不对刷新视图重新进行操作,如果没有更新,则对刷新视图进行操作
*/
private boolean isLoading = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initDatas();
initView();
initValues();
}
/**
* 这里使用handler来进行数据的刷新,因为过郭霖说过,在android3.0之后asyncTask变成了单队列
* 如果两个操作想并发进行,就应该直接用Thread
*/
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
new ScrollTask().execute(-10);
Toast.makeText(getApplicationContext(), "数据刷新完成", Toast.LENGTH_SHORT).show();
};
};
/**
* 初始化各值
*/
private void initValues() {
topParams = (LayoutParams) top.getLayoutParams();
topParams.height = topHeight;
topParams.topMargin = -topHeight;
}
/**
* 初始化界面控件,以及给控件添加监听事件
*/
private void initView() {
listView = (ListView) findViewById(R.id.main_activity_lv);
top = findViewById(R.id.load_layout);
topTV = (TextView) findViewById(R.id.main_activity_top_tv);
topIv = (ImageView) findViewById(R.id.main_activity_top_iv);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, strs);
listView.setAdapter(adapter);
listView.setOnTouchListener(this);
}
/**
* 初始化数据源
*/
private void initDatas() {
for (int i = 0; i < strs.length; i++) {
strs[i] = String.valueOf(i);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
int actionValue = event.getAction();
int margin = 0;
switch (actionValue) {
case MotionEvent.ACTION_DOWN:
downY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//如果没有加载数据则对刷新界面进行操作
if (!isLoading) {
moveY = (int) event.getRawY();
if (listView.getFirstVisiblePosition() == 0 && !isRead) {
isRead = true;
currentY = moveY;
}
if (isRead && currentY >= downY) {
margin = -(topHeight - (moveY - currentY));
changeTop(margin);
}
}
break;
case MotionEvent.ACTION_UP:
isRead = false;
if (!isLoading) {
upY = (int) event.getRawY();
// 计算手指离开的时候刷新视图的margin值
margin = upY - currentY - topHeight;
if (margin < -topHeight) {
margin = -topHeight;
}
if (margin > 0) {
margin = 0;
}
// 如果手指离开的时候margin小于top布局的高度的话则就不进行刷新,否则就进行刷新
if (Math.abs(margin) == 0) {
topIv.setImageResource(R.drawable.qapp_center_ico_loading);
topTV.setText("正在加载...");
Load();
isLoading = true;
} else if (margin > -topHeight) {
new ScrollTask().execute(-10);
isLoading = true;
}
break;
}
}
return false;
}
// 加载数据
private void Load() {
new Thread() {
@Override
public void run() {
try {
// 模拟异步加载数据所消耗的时间
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}.start();
}
/**
* 根据margin的值来改变刷新视图的状态
*
* @param margin
*/
private void changeTop(int margin) {
//如果margin的值大于刷新界面的宽度的话,就把它设置为刷新界面的宽度,因为不能让整个刷新界面离开顶部
if (margin < (-topHeight)) {
margin = (-topHeight);
}
//同理,为保证整个刷新界面不离开顶部,margin也不能大于零
if (margin > 0) {
margin = 0;
topTV.setText("松开进行刷新...");
topIv.setRotation(180F);
}
topParams.topMargin = margin;
top.setLayoutParams(topParams);
}
/**
* 数据加载完成的时候调用,将刷新视图初始化 并将是否正在加载数据的判断属性设置为false
*/
public void loadComplete() {
topIv.setImageResource(R.drawable.esc);
topIv.setRotation(0F);
topTV.setText("下拉刷新...");
isLoading = false;
}
class ScrollTask extends AsyncTask<Integer, Integer, Integer> {
@Override
protected Integer doInBackground(Integer... params) {
int topMargin = topParams.topMargin;
while (true) {
topMargin = topMargin + params[0];
if (topMargin > 0) {
topMargin = 0;
break;
}
if (topMargin < -topHeight) {
topMargin = -topHeight;
break;
}
publishProgress(topMargin);
// 为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动画面
sleep(20);
}
return topMargin;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
topParams.topMargin = values[0];
top.setLayoutParams(topParams);
}
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
topParams.topMargin = result;
top.setLayoutParams(topParams);
loadComplete();
}
/**
* 使当前线程睡眠指定的毫秒数
*
* @param i
*/
private void sleep(int i) {
try {
Thread.sleep(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
里面的代码有点多,我先来说一下主体思路:
里面有几个主要注意的点:判断margin的逻辑有点混乱,还有onTouch方法一定要返回false,因为一旦返回了true的话,会导致listView无法操作,我猜测listView内部应该也实现了OnTouchListener接口,当我们设置OnTouchListener的时候在它内部会对我们传入的listener的返回值进行判断,如果为true就不对listView本身的监听事件进行操作。
到这里基本上就完成了,其中由于没有新的Activity,也没有进行什么需要权限的操作,所以manifest文件里面的代码我们也不用修改。
标签:
原文地址:http://blog.csdn.net/wangpengfei_p/article/details/51420422