当前位置: 编程技术>移动开发
本页文章导读:
▪便利实用的下拉刷新控件,支持ScrollView、AbsListView 方便实用的下拉刷新控件,支持ScrollView、AbsListView
最近要做一个下拉刷新的功能,网上找了很多例子,也看了一些开源的下拉刷新项目,但是小例子比较简单,效果和稳定性都差强人意,而.........
▪ 写在20110613:vendor机制、批改应用编译.mk文件、USB调试 写在20110613:vendor机制、修改应用编译.mk文件、USB调试
1.Vendor机制: 在主分支上建立分量,达到差异化编译的目的,主要体现在***_BUILD和***_CUSTOM两个文件,顾名思义,前者是差异化按.........
▪ 写在20110616:FM主要功能、两个定做、Handler、消息队列 写在20110616:FM主要功能、两个定制、Handler、消息队列
1..txt文本文件短信支持发送2..txt/网页书签的Mimetype均为text/plain,如何区分呢?3.两个定制:Flash U 同时支持内外部存储,不同存储设.........
[1]便利实用的下拉刷新控件,支持ScrollView、AbsListView
来源: 互联网 发布时间: 2014-02-18
方便实用的下拉刷新控件,支持ScrollView、AbsListView
最近要做一个下拉刷新的功能,网上找了很多例子,也看了一些开源的下拉刷新项目,但是小例子比较简单,效果和稳定性都差强人意,而开源的项目又太庞大,看起来耗时费劲,所以只好综合一下各处的代码掌握其原理,自己实现一套下拉刷新功能。
该控件特点:
1.子控件必须是一个ScrollView或ListView;
2.支持自定义下拉布局;
3.自定义下拉布局可以不用处理下拉的各种状态(只需要实现几个接口即可),也可以自己处理各种下拉的状态。
先来看看效果图:
上代码:
首先看如何使用:
1.使用的布局:
<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" >
<com.example.pulldown.PullDownScrollView
android:id="@+id/refresh_root"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:background="#161616"
android:orientation="vertical" >
<ScrollView
android:id="@+id/scrollview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="none" >
<LinearLayout
android:id="@+id/mainView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#1f1f1f"
android:orientation="vertical" >
<!-- 自已的布局 -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="@android:color/white"
android:textSize="18sp" />
</LinearLayout>
</ScrollView>
</com.example.pulldown.PullDownScrollView>
</LinearLayout>
2.UI使用:
首先,Activity实现接口:
implements RefreshListener
部分代码如下:
package com.example.pulldown;
import com.example.pulldown.PullDownScrollView.RefreshListener;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity implements RefreshListener{
private PullDownScrollView mPullDownScrollView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPullDownScrollView = (PullDownScrollView) findViewById(R.id.refresh_root);
mPullDownScrollView.setRefreshListener(this);
mPullDownScrollView.setPullDownElastic(new PullDownElasticImp(this));
}
@Override
public void onRefresh(PullDownScrollView view) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mPullDownScrollView.finishRefresh("上次刷新时间:12:23");
}
}, 2000);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
3.再来看看控件代码:
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
/**
* @author xwangly@163.com
* @date 2013-7-9
*
*/
public class PullDownScrollView extends LinearLayout {
private static final String TAG = "PullDownScrollView";
private int refreshTargetTop = -60;
private int headContentHeight;
private RefreshListener refreshListener;
private RotateAnimation animation;
private RotateAnimation reverseAnimation;
private final static int RATIO = 2;
private int preY = 0;
private boolean isElastic = false;
private int startY;
private int state;
private String note_release_to_refresh = "松开更新";
private String note_pull_to_refresh = "下拉刷新";
private String note_refreshing = "正在更新...";
private IPullDownElastic mElastic;
public PullDownScrollView(Context context) {
super(context);
init();
}
public PullDownScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
animation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(250);
animation.setFillAfter(true);
reverseAnimation = new RotateAnimation(-180, 0,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
reverseAnimation.setInterpolator(new LinearInterpolator());
reverseAnimation.setDuration(200);
reverseAnimation.setFillAfter(true);
}
/**
* 刷新监听
* @param listener
*/
public void setRefreshListener(RefreshListener listener) {
this.refreshListener = listener;
}
/**
* 下拉布局
* @param elastic
*/
public void setPullDownElastic(IPullDownElastic elastic) {
mElastic = elastic;
headContentHeight = mElastic.getElasticHeight();
refreshTargetTop = - headContentHeight;
LayoutParams lp = new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, headContentHeight);
lp.topMargin = refreshTargetTop;
addView(mElastic.getElasticLayout(), 0, lp);
}
/**
* 设置更新提示语
* @param pullToRefresh 下拉刷新提示语
* @param releaseToRefresh 松开刷新提示语
* @param refreshing 正在刷新提示语
*/
public void setRefreshTips(String pullToRefresh, String releaseToRefresh, String refreshing) {
note_pull_to_refresh = pullToRefresh;
note_release_to_refresh = releaseToRefresh;
note_refreshing = refreshing;
}
/*
* 该方法一般和ontouchEvent 一起用 (non-Javadoc)
*
* @see
* android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Logger.d(TAG, "onInterceptTouchEvent");
printMotionEvent(ev);
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
preY = (int) ev.getY();
}
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
Logger.d(TAG, "isElastic:" + isElastic + " canScroll:"+ canScroll() + " ev.getY() - preY:"+(ev.getY() - preY));
if (!isElastic && canScroll()
&& (int) ev.getY() - preY >= headContentHeight / (3*RATIO)
&& refreshListener != null && mElastic != null) {
isElastic = true;
startY = (int) ev.getY();
Logger.i(TAG, "在move时候记录下位置startY:" + startY);
return true;
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Logger.d(TAG, "onTouchEvent");
printMotionEvent(event);
handleHeadElastic(event);
return super.onTouchEvent(event);
}
private void handleHeadElastic(MotionEvent event) {
if (refreshListener != null && mElastic != null) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Logger.i(TAG, "down");
break;
case MotionEvent.ACTION_UP:
Logger.i(TAG, "up");
if (state != IPullDownElastic.REFRESHING && isElastic) {
if (state == IPullDownElastic.DONE) {
// 什么都不做
setMargin(refreshTargetTop);
}
if (state == IPullDownElastic.PULL_To_REFRESH) {
state = IPullDownElastic.DONE;
setMargin(refreshTargetTop);
changeHeaderViewByState(state, false);
Logger.i(TAG, "由下拉刷新状态,到done状态");
}
if (state == IPullDownElastic.RELEASE_To_REFRESH) {
state = IPullDownElastic.REFRESHING;
setMargin(0);
changeHeaderViewByState(state, false);
onRefresh();
Logger.i(TAG, "由松开刷新状态,到done状态");
}
}
isElastic = false;
break;
case MotionEvent.ACTION_MOVE:
Logger.i(TAG, "move");
int tempY = (int) event.getY();
if (state != IPullDownElastic.REFRESHING && isElastic) {
// 可以松手去刷新了
if (state == IPullDownElastic.RELEASE_To_REFRESH) {
if (((tempY - startY) / RATIO < headContentHeight)
&& (tempY - startY) > 0) {
state = IPullDownElastic.PULL_To_REFRESH;
changeHeaderViewByState(state, true);
Logger.i(TAG, "由松开刷新状态转变到下拉刷新状态");
} else if (tempY - startY <= 0) {
state = IPullDownElastic.DONE;
changeHeaderViewByState(state, false);
Logger.i(TAG, "由松开刷新状态转变到done状态");
}
}
if (state == IPullDownElastic.DONE) {
if (tempY - startY > 0) {
state = IPullDownElastic.PULL_To_REFRESH;
changeHeaderViewByState(state, false);
}
}
if (state == IPullDownElastic.PULL_To_REFRESH) {
// 下拉到可以进入RELEASE_TO_REFRESH的状态
if ((tempY - startY) / RATIO >= headContentHeight) {
state = IPullDownElastic.RELEASE_To_REFRESH;
changeHeaderViewByState(state, false);
Logger.i(TAG, "由done或者下拉刷新状态转变到松开刷新");
} else if (tempY - startY <= 0) {
state = IPullDownElastic.DONE;
changeHeaderViewByState(state, false);
Logger.i(TAG, "由DOne或者下拉刷新状态转变到done状态");
}
}
if (tempY - startY > 0) {
setMargin((tempY - startY)/2 + refreshTargetTop);
}
}
break;
}
}
}
/**
*
*/
private void setMargin(int top) {
LinearLayout.LayoutParams lp = (LayoutParams) mElastic.getElasticLayout()
.getLayoutParams();
lp.topMargin = top;
// 修改后刷新
mElastic.getElasticLayout().setLayoutParams(lp);
mElastic.getElasticLayout().invalidate();
}
private void changeHeaderViewByState(int state, boolean isBack) {
mElastic.changeElasticState(state, isBack);
switch (state) {
case IPullDownElastic.RELEASE_To_REFRESH:
mElastic.showArrow(View.VISIBLE);
mElastic.showProgressBar(View.GONE);
mElastic.showLastUpdate(View.VISIBLE);
mElastic.setTips(note_release_to_refresh);
mElastic.clearAnimation();
mElastic.startAnimation(animation);
Logger.i(TAG, "当前状态,松开刷新");
break;
case IPullDownElastic.PULL_To_REFRESH:
mElastic.showArrow(View.VISIBLE);
mElastic.showProgressBar(View.GONE);
mElastic.showLastUpdate(View.VISIBLE);
mElastic.setTips(note_pull_to_refresh);
mElastic.clearAnimation();
// 是由RELEASE_To_REFRESH状态转变来的
if (isBack) {
mElastic.startAnimation(reverseAnimation);
}
Logger.i(TAG, "当前状态,下拉刷新");
break;
case IPullDownElastic.REFRESHING:
mElastic.showArrow(View.GONE);
mElastic.showProgressBar(View.VISIBLE);
mElastic.showLastUpdate(View.GONE);
mElastic.setTips(note_refreshing);
mElastic.clearAnimation();
Logger.i(TAG, "当前状态,正在刷新...");
break;
case IPullDownElastic.DONE:
mElastic.showProgressBar(View.GONE);
mElastic.clearAnimation();
// arrowImageView.setImageResource(R.drawable.goicon);
// tipsTextview.setText("下拉刷新");
// lastUpdatedTextView.setVisibility(View.VISIBLE);
Logger.i(TAG, "当前状态,done");
break;
}
}
private void onRefresh() {
// downTextView.setVisibility(View.GONE);
// scroller.startScroll(0, i, 0, 0 - i);
// invalidate();
if (refreshListener != null) {
refreshListener.onRefresh(this);
}
}
/**
*
*/
@Override
public void computeScroll() {
// if (scroller.computeScrollOffset()) {
// int i = this.scroller.getCurrY();
// LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) this.refreshView
// .getLayoutParams();
// int k = Math.max(i, refreshTargetTop);
// lp.topMargin = k;
// this.refreshView.setLayoutParams(lp);
// this.refreshView.invalidate();
// invalidate();
// }
}
/**
* 结束刷新事件,UI刷新完成后必须回调此方法
* @param text 一般传入:“上次更新时间:12:23”
*/
public void finishRefresh(String text) {
if (mElastic == null) {
Logger.d(TAG, "finishRefresh mElastic:" + mElastic);
return;
}
state = IPullDownElastic.DONE;
mElastic.setLastUpdateText(text);
changeHeaderViewByState(state,false);
Logger.i(TAG, "执行了=====finishRefresh");
mElastic.showArrow(View.VISIBLE);
mElastic.showLastUpdate(View.VISIBLE);
setMargin(refreshTargetTop);
// scroller.startScroll(0, i, 0, refreshTargetTop);
// invalidate();
}
private boolean canScroll() {
View childView;
if (getChildCount() > 1) {
childView = this.getChildAt(1);
if (childView instanceof AbsListView) {
int top = ((AbsListView) childView).getChildAt(0).getTop();
int pad = ((AbsListView) childView).getListPaddingTop();
if ((Math.abs(top - pad)) < 3
&& ((AbsListView) childView).getFirstVisiblePosition() == 0) {
return true;
} else {
return false;
}
} else if (childView instanceof ScrollView) {
if (((ScrollView) childView).getScrollY() == 0) {
return true;
} else {
return false;
}
}
}
return canScroll(this);
}
/**
* 子类重写此方法可以兼容其它的子控件,目前只兼容AbsListView和ScrollView
* @param view
* @return
*/
public boolean canScroll(PullDownScrollView view) {
return false;
}
private void printMotionEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Logger.d(TAG, "down");
break;
case MotionEvent.ACTION_MOVE:
Logger.d(TAG, "move");
break;
case MotionEvent.ACTION_UP:
Logger.d(TAG, "up");
default:
break;
}
}
/**
* 刷新监听接口
*/
public interface RefreshListener {
public void onRefresh(PullDownScrollView view);
}
}
4.接口:
import android.view.View;
import android.view.animation.Animation;
/**
* @author xwangly@163.com
* @date 2013-7-10
* 下拉控件接口
*/
public interface IPullDownElastic {
public final static int RELEASE_To_REFRESH = 0;
public final static int PULL_To_REFRESH = 1;
public final static int REFRESHING = 2;
public final static int DONE = 3;
public View getElasticLayout();
public int getElasticHeight();
public void showArrow(int visibility);
public void startAnimation(Animation animation);
public void clearAnimation();
public void showProgressBar(int visibility);
public void setTips(String tips);
public void showLastUpdate(int visibility);
public void setLastUpdateText(String text);
/**
* 可以不用实现此方法,PullDownScrollView会处理ElasticLayout布局中的状态
* 如果需要特殊处理,可以实现此方法进行处理
*
* @param state @see RELEASE_To_REFRESH
* @param isBack 是否是松开回退
*/
public void changeElasticState(int state, boolean isBack);
}
5.默认实现:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
* @author xwangly@163.com
* @date 2013-7-10
* 默认下拉控件布局实现
*/
public class PullDownElasticImp implements IPullDownElastic {
private View refreshView;
private ImageView arrowImageView;
private int headContentHeight;
private ProgressBar progressBar;
private TextView tipsTextview;
private TextView lastUpdatedTextView;
private Context mContext;
public PullDownElasticImp(Context context) {
mContext = context;
init();
}
private void init() {
// 刷新视图顶端的的view
refreshView = LayoutInflater.from(mContext).inflate(
R.layout.refresh_top_item, null);
// 指示器view
arrowImageView = (ImageView) refreshView
.findViewById(R.id.head_arrowImageView);
// 刷新bar
progressBar = (ProgressBar) refreshView
.findViewById(R.id.head_progressBar);
// 下拉显示text
tipsTextview = (TextView) refreshView.findViewById(R.id.refresh_hint);
// 下来显示时间
lastUpdatedTextView = (TextView) refreshView
.findViewById(R.id.refresh_time);
headContentHeight = Utils.dip2px(mContext, 50);
}
/**
* @return
*
*/
@Override
public View getElasticLayout() {
return refreshView;
}
/**
* @return
*
*/
@Override
public int getElasticHeight() {
return headContentHeight;
}
/**
* @param show
*
*/
@Override
public void showArrow(int visibility) {
arrowImageView.setVisibility(visibility);
}
/**
* @param animation
*
*/
@Override
public void startAnimation(Animation animation) {
arrowImageView.startAnimation(animation);
}
/**
*
*
*/
@Override
public void clearAnimation() {
arrowImageView.clearAnimation();
}
/**
* @param show
*
*/
@Override
public void showProgressBar(int visibility) {
progressBar.setVisibility(visibility);
}
/**
* @param tips
*
*/
@Override
public void setTips(String tips) {
tipsTextview.setText(tips);
}
/**
* @param show
*
*/
@Override
public void showLastUpdate(int visibility) {
lastUpdatedTextView.setVisibility(visibility);
}
/**
* @param text
*
*/
public void setLastUpdateText(String text) {
lastUpdatedTextView.setText(text);
}
/**
* @param state
* @param isBack
*
*/
@Override
public void changeElasticState(int state, boolean isBack) {
// TODO Auto-generated method stub
}
}
6.默认实现的布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="-50.0dip"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0.0dip"
android:layout_weight="1.0"
android:gravity="center"
android:orientation="horizontal" >
<!-- 箭头图像、进度条 -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="30dip" >
<!-- 箭头 -->
<ImageView
android:id="@+id/head_arrowImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="/blog_article/@drawable/goicon/index.html" />
<!-- 进度条 -->
<ProgressBar
android:id="@+id/head_progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<!-- 提示 -->
<TextView
android:id="@+id/refresh_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="#f2f2f2"
android:textSize="16sp" />
<!-- 最近更新 -->
<TextView
android:id="@+id/refresh_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上次更新"
android:textColor="#b89766"
android:textSize="10sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
6.图片资源:
@drawable/goicon
完结
修改:从原工程中独立出来,并附上简单的工程,见附件。
1 楼
freezingsky
19 小时前
写得很好。继续努力!
[2] 写在20110613:vendor机制、批改应用编译.mk文件、USB调试
来源: 互联网 发布时间: 2014-02-18
写在20110613:vendor机制、修改应用编译.mk文件、USB调试
1.Vendor机制:
在主分支上建立分量,达到差异化编译的目的,主要体现在***_BUILD和***_CUSTOM两个文件,顾名思义,前者是差异化按项目分门别类的编译脚本,后者是按项目分类的各应用相关定制的文件抽取存放位置
2.是否把某个应用编译进去,可以在该应用对应的工程目录下的.mk文件中修改脚本
3.ADB设备识别不了:检查手机驱动是否安装、设置中“允许USB调试”选项是否开启。
1.Vendor机制:
在主分支上建立分量,达到差异化编译的目的,主要体现在***_BUILD和***_CUSTOM两个文件,顾名思义,前者是差异化按项目分门别类的编译脚本,后者是按项目分类的各应用相关定制的文件抽取存放位置
2.是否把某个应用编译进去,可以在该应用对应的工程目录下的.mk文件中修改脚本
3.ADB设备识别不了:检查手机驱动是否安装、设置中“允许USB调试”选项是否开启。
[3] 写在20110616:FM主要功能、两个定做、Handler、消息队列
来源: 互联网 发布时间: 2014-02-18
写在20110616:FM主要功能、两个定制、Handler、消息队列
1..txt文本文件短信支持发送
2..txt/网页书签的Mimetype均为text/plain,如何区分呢?
3.两个定制:Flash U 同时支持内外部存储,不同存储设备之间相互拷贝等操作
DRM 用到第三方API或者自研的API
4.MessageQueue和Handler、Looper
4.1 Message
Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
4.2 Handler
Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
4.3 Message Queue
Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
4.4 Looper
Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,查看API Doc
1..txt文本文件短信支持发送
2..txt/网页书签的Mimetype均为text/plain,如何区分呢?
3.两个定制:Flash U 同时支持内外部存储,不同存储设备之间相互拷贝等操作
DRM 用到第三方API或者自研的API
4.MessageQueue和Handler、Looper
4.1 Message
Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
4.2 Handler
Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
4.3 Message Queue
Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
4.4 Looper
Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,查看API Doc
最新技术文章: