- 浏览: 361499 次
- 性别:
- 来自: 南京
文章分类
最新评论
-
nglxl:
点赞啊,必须点赞!!深度好文,解决了困扰我多年的疑惑。
tcp 长连接与短连接 -
cofftech:
opengl源码http://www.eyesourcecod ...
OPenGL ES 关键API小结 -
mail_j:
不是很好用,很多情况都不能处理 1、没有区分关键字的大小写2、 ...
Java解析sql语句,分析出调用到的所有表 -
a455642158:
就算监听到变成了cmwap还不行,还得监听网络连接状态Stat ...
android APN切换cmwap实现 -
xuhl1022:
看了您的android 4篇 浏览器二次开发,感触颇深,写的很 ...
Android浏览器Browser二次开发(四)浏览器中的APN切换
大家看到新浪微博中的列表往下拉的时候, 头部出现正在刷新,然后获取到数据了以后列表会刷新的效果了吧,是不是都觉得很棒啊。 笔者今天参考进行实现。 当然也借鉴了前人的劳动成果。 下面对具体实现进行剖析。
首先要明确一点的是,ListView提供了HeaderView 和FooterView用于增加顶部和底部视图。 头部的刷新调和底部的更多条目都是使用这两个来实现的。
下面我们来看看怎么实现的:
1. 对ListView进行集成,主要是监听滑动事件,触发底端和顶端事件:
package com.drawthink.nxjobhunting.ui.components;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
/**
* <p>一个可以监听ListView是否滚动到最顶部或最底部的自定义控件</p>
* 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br>
* 如果加以改进,可以实现监听scroll滚动的具体位置等
* @author Seya Qu</br> Email:seyason@gmail.com
*/
public class ScrollOverListView extends ListView {
private int mLastY;
private int mTopPosition;
private int mBottomPosition;
public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public ScrollOverListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScrollOverListView(Context context) {
super(context);
init();
}
private void init(){
mTopPosition = 0;
mBottomPosition = 0;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final int y = (int) ev.getRawY();
switch(action){
case MotionEvent.ACTION_DOWN:{
mLastY = y;
final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);
if (isHandled) {
mLastY = y;
return isHandled;
}
break;
}
case MotionEvent.ACTION_MOVE:{
final int childCount = getChildCount();
if(childCount == 0) return super.onTouchEvent(ev);
final int itemCount = getAdapter().getCount() - mBottomPosition;
final int deltaY = y - mLastY;
//DLog.d("lastY=%d y=%d", mLastY, y);
final int firstTop = getChildAt(0).getTop();
final int listPadding = getListPaddingTop();
final int lastBottom = getChildAt(childCount - 1).getBottom();
final int end = getHeight() - getPaddingBottom();
final int firstVisiblePosition = getFirstVisiblePosition();
final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY);
if(isHandleMotionMove){
mLastY = y;
return true;
}
//DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);
if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {
final boolean isHandleOnListViewTopAndPullDown;
isHandleOnListViewTopAndPullDown = mOnScrollOverListener.onListViewTopAndPullDown(deltaY);
if(isHandleOnListViewTopAndPullDown){
mLastY = y;
return true;
}
}
// DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);
if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {
final boolean isHandleOnListViewBottomAndPullDown;
isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY);
if(isHandleOnListViewBottomAndPullDown){
mLastY = y;
return true;
}
}
break;
}
case MotionEvent.ACTION_UP:{
final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev);
if (isHandlerMotionUp) {
mLastY = y;
return true;
}
break;
}
}
mLastY = y;
return super.onTouchEvent(ev);
}
/**空的*/
private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){
@Override
public boolean onListViewTopAndPullDown(int delta) {
return false;
}
@Override
public boolean onListViewBottomAndPullUp(int delta) {
return false;
}
@Override
public boolean onMotionDown(MotionEvent ev) {
return false;
}
@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
return false;
}
@Override
public boolean onMotionUp(MotionEvent ev) {
return false;
}
};
// =============================== public method ===============================
/**
* 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个
*
* @param index 正数第几个,必须在条目数范围之内
*/
public void setTopPosition(int index){
if(getAdapter() == null)
throw new NullPointerException("You must set adapter before setTopPosition!");
if(index < 0)
throw new IllegalArgumentException("Top position must > 0");
mTopPosition = index;
}
/**
* 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个
*
* @param index 倒数第几个,必须在条目数范围之内
*/
public void setBottomPosition(int index){
if(getAdapter() == null)
throw new NullPointerException("You must set adapter before setBottonPosition!");
if(index < 0)
throw new IllegalArgumentException("Bottom position must > 0");
mBottomPosition = index;
}
/**
* 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br>
*
* @see OnScrollOverListener
*/
public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener){
mOnScrollOverListener = onScrollOverListener;
}
/**
* 滚动监听接口</br>
* @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)
*
* @author Seya Qu</br> Email:seyason@gmail.com
*/
public interface OnScrollOverListener {
/**
* 到达最顶部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewTopAndPullDown(int delta);
/**
* 到达最底部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewBottomAndPullUp(int delta);
/**
* 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionDown(MotionEvent ev);
/**
* 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionMove(MotionEvent ev, int delta);
/**
* 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionUp(MotionEvent ev);
}
}
2. 定义PullDownView, 继承LinearLayout, 主要是为了调用和监听ListView, 通过接口OnPullDownListener的onRefresh来通知外部进行顶部数据的刷新, onMore触发请求底部更多数据, 具体如何去获取数据, 需要在外面的Activity中实现。
package com.drawthink.nxjobhunting.ui.components;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.drawthink.nxjobhunting.R;
import com.drawthink.nxjobhunting.ui.components.ScrollOverListView.OnScrollOverListener;
import com.drawthink.nxjobhunting.utils.GlobalVar;
import com.drawthink.nxjobhunting.utils.LogX;
/**
* 下拉刷新控件</br>
* 真正实现下拉刷新的是这个控件,
* ScrollOverListView只是提供触摸的事件等
* @author Seya Qu</br> Email:seyason@gmail.com
*/
public class PullDownView extends LinearLayout implements OnScrollOverListener{
private static final int START_PULL_DEVIATION = 50; // 移动误差
private static final int AUTO_INCREMENTAL = 10; // 自增量,用于回弹
private static final int WHAT_DID_LOAD_DATA = 1; // Handler what 数据加载完毕
private static final int WHAT_ON_REFRESH = 2; // Handler what 刷新中
private static final int WHAT_DID_REFRESH = 3; // Handler what 已经刷新完
private static final int WHAT_SET_HEADER_HEIGHT = 4;// Handler what 设置高度
private static final int WHAT_DID_MORE = 5; // Handler what 已经获取完更多
private static final int DEFAULT_HEADER_VIEW_HEIGHT = 105; // 头部文件原本的高度
private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm");
private View mHeaderView;
private LayoutParams mHeaderViewParams;
private TextView mHeaderViewDateView;
private TextView mHeaderTextView;
private ImageView mHeaderArrowView;
private View mHeaderLoadingView;
private View mFooterView;
private TextView mFooterTextView;
private View mFooterLoadingView;
private ScrollOverListView mListView;
private OnPullDownListener mOnPullDownListener;
private RotateAnimation mRotateOTo180Animation;
private RotateAnimation mRotate180To0Animation;
private int mHeaderIncremental; // 增量
private float mMotionDownLastY; // 按下时候的Y轴坐标
private boolean mIsDown; // 是否按下
private boolean mIsRefreshing; // 是否下拉刷新中
private boolean mIsFetchMoreing; // 是否获取更多中
private boolean mIsPullUpDone; // 是否回推完成
private boolean mEnableAutoFetchMore; // 是否允许自动获取更多
// 头部文件的状态
private static final int HEADER_VIEW_STATE_IDLE = 0; // 空闲
private static final int HEADER_VIEW_STATE_NOT_OVER_HEIGHT = 1; // 没有超过默认高度
private static final int HEADER_VIEW_STATE_OVER_HEIGHT = 2; // 超过默认高度
private int mHeaderViewState = HEADER_VIEW_STATE_IDLE;
public PullDownView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeaderViewAndFooterViewAndListView(context);
}
public PullDownView(Context context) {
super(context);
initHeaderViewAndFooterViewAndListView(context);
}
/*
* ==================================
* Public method
* 外部使用,具体就是用这几个就可以了
*
* ==================================
*/
/**
* 刷新事件接口
* @author 何龙威 Email:kjsoloho@gmail.com
*/
public interface OnPullDownListener {
void onRefresh();
void onMore();
}
/**
* 通知加载完了数据,要放在Adapter.notifyDataSetChanged后面
* 当你加载完数据的时候,调用这个notifyDidLoad()
* 才会隐藏头部,并初始化数据等
*/
public void notifyDidLoad() {
mUIHandler.sendEmptyMessage(WHAT_DID_LOAD_DATA);
}
/**
* 通知已经刷新完了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完刷新任务之后,调用这个notifyDidRefresh()
* 才会隐藏掉头部文件等操作
*/
public void notifyDidRefresh() {
mUIHandler.sendEmptyMessage(WHAT_DID_REFRESH);
}
/**
* 通知已经获取完更多了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完更多任务之后,调用这个notyfyDidMore()
* 才会隐藏加载圈等操作
*/
public void notifyDidMore() {
mUIHandler.sendEmptyMessage(WHAT_DID_MORE);
}
/**
* 设置监听器
* @param listener
*/
public void setOnPullDownListener(OnPullDownListener listener){
mOnPullDownListener = listener;
}
/**
* 获取内嵌的listview
* @return ScrollOverListView
*/
public ListView getListView(){
return mListView;
}
/**
* 是否开启自动获取更多
* 自动获取更多,将会隐藏footer,并在到达底部的时候自动刷新
* @param index 倒数第几个触发
*/
public void enableAutoFetchMore(boolean enable, int index){
if(enable){
mListView.setBottomPosition(index);
mFooterLoadingView.setVisibility(View.VISIBLE);
}else{
mFooterTextView.setText("更多");
mFooterLoadingView.setVisibility(View.GONE);
}
mEnableAutoFetchMore = enable;
}
/*
* ==================================
* Private method
* 具体实现下拉刷新等操作
*
* ==================================
*/
/**
* 初始化界面
*/
private void initHeaderViewAndFooterViewAndListView(Context context){
setOrientation(LinearLayout.VERTICAL);
//setDrawingCacheEnabled(false);
/*
* 自定义头部文件
* 放在这里是因为考虑到很多界面都需要使用
* 如果要修改,和它相关的设置都要更改
*/
mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);
mHeaderViewParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
addView(mHeaderView, 0, mHeaderViewParams);
mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);
mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);
mHeaderLoadingView = mHeaderView.findViewById(R.id.pulldown_header_loading);
// 注意,图片旋转之后,再执行旋转,坐标会重新开始计算
mRotateOTo180Animation = new RotateAnimation(0, 180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotateOTo180Animation.setDuration(250);
mRotateOTo180Animation.setFillAfter(true);
mRotate180To0Animation = new RotateAnimation(180, 0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotate180To0Animation.setDuration(250);
mRotate180To0Animation.setFillAfter(true);
/**
* 自定义脚部文件
*/
mFooterView = LayoutInflater.from(context).inflate(R.layout.pulldown_footer, null);
mFooterTextView = (TextView) mFooterView.findViewById(R.id.pulldown_footer_text);
mFooterLoadingView = mFooterView.findViewById(R.id.pulldown_footer_loading);
mFooterView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(!mIsFetchMoreing){
mIsFetchMoreing = true;
mFooterTextView.setText("加载更多中...");
mFooterLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onMore();
}
}
});
/*
* ScrollOverListView 同样是考虑到都是使用,所以放在这里
* 同时因为,需要它的监听事件
*/
mListView = new ScrollOverListView(context);
mListView.setVerticalScrollBarEnabled(false);
mListView.setOnScrollOverListener(this);
mListView.setCacheColorHint(0);
mListView.setDivider(GlobalVar.appContext.getResources().getDrawable(R.drawable.dot));
addView(mListView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
// 空的listener
mOnPullDownListener = new OnPullDownListener() {
@Override
public void onRefresh() {}
@Override
public void onMore() {}
};
}
/**
* 在下拉和回推的时候检查头部文件的状态</br>
* 如果超过了默认高度,就显示松开可以刷新,
* 否则显示下拉可以刷新
*/
private void checkHeaderViewState(){
if(mHeaderViewParams.height >= DEFAULT_HEADER_VIEW_HEIGHT){
if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;
mHeaderViewState = HEADER_VIEW_STATE_OVER_HEIGHT;
mHeaderTextView.setText("松开可以刷新");
mHeaderArrowView.startAnimation(mRotateOTo180Animation);
}else{
if(mHeaderViewState == HEADER_VIEW_STATE_NOT_OVER_HEIGHT
|| mHeaderViewState == HEADER_VIEW_STATE_IDLE) return;
mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;
mHeaderTextView.setText("下拉可以刷新");
mHeaderArrowView.startAnimation(mRotate180To0Animation);
}
}
private void setHeaderHeight(final int height){
mHeaderIncremental = height;
mHeaderViewParams.height = height;
mHeaderView.setLayoutParams(mHeaderViewParams);
}
/**
* 自动隐藏动画
* @author 何龙威 Email:kjsoloho@gmail.com
*/
class HideHeaderViewTask extends TimerTask{
@Override
public void run() {
if(mIsDown) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > 0){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = 0;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
cancel();
}
}
}
/**
* 自动显示动画
* @author 何龙威 Email:kjsoloho@gmail.com
*/
class ShowHeaderViewTask extends TimerTask{
@Override
public void run() {
if(mIsDown) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > DEFAULT_HEADER_VIEW_HEIGHT){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = DEFAULT_HEADER_VIEW_HEIGHT;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
if(!mIsRefreshing){
mIsRefreshing = true;
mUIHandler.sendEmptyMessage(WHAT_ON_REFRESH);
}
cancel();
}
}
}
private Handler mUIHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case WHAT_DID_LOAD_DATA:{
mHeaderViewParams.height = 0;
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderTextView.setText("下拉可以刷新");
mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);
mHeaderViewDateView.setVisibility(View.VISIBLE);
mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
mHeaderArrowView.setVisibility(View.VISIBLE);
showFooterView();
return;
}
case WHAT_ON_REFRESH:{
// 要清除掉动画,否则无法隐藏
mHeaderArrowView.clearAnimation();
mHeaderArrowView.setVisibility(View.INVISIBLE);
mHeaderLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onRefresh();
return;
}
case WHAT_DID_REFRESH :{
mIsRefreshing = false;
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
mHeaderArrowView.setVisibility(View.VISIBLE);
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
setHeaderHeight(0);
showFooterView();
return;
}
case WHAT_SET_HEADER_HEIGHT :{
setHeaderHeight(mHeaderIncremental);
return;
}
case WHAT_DID_MORE :{
mIsFetchMoreing = false;
mFooterTextView.setText("更多");
mFooterLoadingView.setVisibility(View.GONE);
}
}
}
};
/**
* 显示脚步脚部文件
*/
private void showFooterView(){
if(mListView.getFooterViewsCount() == 0 && isFillScreenItem()){
mListView.addFooterView(mFooterView);
mListView.setAdapter(mListView.getAdapter());
}
}
/**
* 条目是否填满整个屏幕
*/
private boolean isFillScreenItem(){
final int firstVisiblePosition = mListView.getFirstVisiblePosition();
final int lastVisiblePostion = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();
final int visibleItemCount = lastVisiblePostion - firstVisiblePosition + 1;
final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();
if(visibleItemCount < totalItemCount) return true;
return false;
}
/*
* ==================================
* 实现 OnScrollOverListener接口
*
*
* ==================================
*/
@Override
public boolean onListViewTopAndPullDown(int delta) {
if(mIsRefreshing || mListView.getCount() - mListView.getFooterViewsCount() == 0) return false;
int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);
mHeaderIncremental += i;
if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight
setHeaderHeight(mHeaderIncremental);
checkHeaderViewState();
}
return true;
}
@Override
public boolean onListViewBottomAndPullUp(int delta) {
if(!mEnableAutoFetchMore || mIsFetchMoreing) return false;
// 数量充满屏幕才触发
if(isFillScreenItem()){
mIsFetchMoreing = true;
mFooterTextView.setText("加载更多中...");
mFooterLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onMore();
return true;
}
return false;
}
@Override
public boolean onMotionDown(MotionEvent ev) {
mIsDown = true;
mIsPullUpDone = false;
mMotionDownLastY = ev.getRawY();
return false;
}
@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
//当头部文件回推消失的时候,不允许滚动
if(mIsPullUpDone) return true;
// 如果开始按下到滑动距离不超过误差值,则不滑动
final int absMotionY = (int) Math.abs(ev.getRawY() - mMotionDownLastY);
if(absMotionY < START_PULL_DEVIATION) return true;
final int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);
// onTopDown在顶部,并上回推和onTopUp相对
if(mHeaderViewParams.height > 0 && delta < 0){
mHeaderIncremental -= i;
if(mHeaderIncremental > 0){
setHeaderHeight(mHeaderIncremental);
checkHeaderViewState();
}else{
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
mHeaderIncremental = 0;
setHeaderHeight(mHeaderIncremental);
mIsPullUpDone = true;
}
return true;
}
return false;
}
@Override
public boolean onMotionUp(MotionEvent ev) {
mIsDown = false;
// 避免和点击事件冲突
if(mHeaderViewParams.height > 0){
// 判断头文件拉动的距离与设定的高度,小了就隐藏,多了就固定高度
int x = mHeaderIncremental - DEFAULT_HEADER_VIEW_HEIGHT;
Timer timer = new Timer(true);
if(x < 0){
timer.scheduleAtFixedRate(new HideHeaderViewTask(), 0, 10);
}else{
timer.scheduleAtFixedRate(new ShowHeaderViewTask(), 0, 10);
}
return true;
}
return false;
}
}
3. xml布局文件Layout中的定义
pulldown_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingTop="10dp" >
<TextView
android:id="@+id/pulldown_footer_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="更多"
android:textSize="15dp" />
<ProgressBar
android:id="@+id/pulldown_footer_loading"
style="@android:style/Widget.ProgressBar.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:visibility="gone" />
</RelativeLayout>
pulldown_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingTop="10dp" >
<ImageView
android:id="@+id/pulldown_header_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="20dp"
android:scaleType="centerCrop"
android:src="@drawable/z_arrow_down"
android:visibility="invisible" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/pulldown_header_arrow"
android:layout_alignTop="@+id/pulldown_header_arrow"
android:layout_centerHorizontal="true"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/pulldown_header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="加载中..." />
<TextView
android:id="@+id/pulldown_header_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="更新于:"
android:visibility="gone" />
</LinearLayout>
<ProgressBar
android:id="@+id/pulldown_header_loading"
style="@android:style/Widget.ProgressBar.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp" />
</RelativeLayout>
调用自定义的PullDownView部分布局文件
um_information_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:background="#FFFFFF" android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
...
<com.drawthink.nxjobhunting.ui.components.PullDownView
android:id="@+id/latest_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="@android:color/white"
android:layout_below="@id/category_layout"
>
</com.drawthink.nxjobhunting.ui.components.PullDownView>
</RelativeLayout>
4. Activity文件
JobsInformationActivity
package com.drawthink.nxjobhunting.ui;
import java.util.ArrayList;
import java.util.List;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import com.drawthink.nxjobhunting.R;
import com.drawthink.nxjobhunting.handler.HandlerManager;
import com.drawthink.nxjobhunting.http.response.CateListRespProcessor;
import com.drawthink.nxjobhunting.http.response.LatestJobsRespProcessor;
import com.drawthink.nxjobhunting.logic.CategoryManager;
import com.drawthink.nxjobhunting.logic.JobsManager;
import com.drawthink.nxjobhunting.ui.adapter.CategoryExpListAdapter;
import com.drawthink.nxjobhunting.ui.adapter.JobsListAdapter;
import com.drawthink.nxjobhunting.ui.components.PullDownView;
import com.drawthink.nxjobhunting.ui.components.PullDownView.OnPullDownListener;
import com.drawthink.nxjobhunting.utils.Constants;
import com.drawthink.nxjobhunting.utils.GlobalKeys;
import com.drawthink.nxjobhunting.vo.CategoryItem;
import com.drawthink.nxjobhunting.vo.JobInfo;
public class JobsInformationActivity extends BaseActivity implements OnPullDownListener, OnItemClickListener{
public static enum TabIndex{
LATEST,
FULL_TIME,
PART_TIME
}
private CategoryManager mManager;
private JobsManager jobManager;
// private ToolBar mToolBar;
private Button latest;
private Button fullTime;
private Button partTime;
private CategoryExpListAdapter mAdapter;
private ExpandableListView exListV;
private Button searchBtn;
private EditText searchInput;
private LinearLayout catelogL;
private RelativeLayout loadingL;
//最新职位列表
private PullDownView mPullDownView;
private ListView mListView;
private JobsListAdapter jobListAdapter;
private List<JobInfo> listData;
private TabIndex tabIndex = TabIndex.LATEST;
private String lastRecTime = "";
//keep category list in memory
public static List<CategoryItem> fullTimeGList = null;
public static List<CategoryItem> partTimeGList = null;
private OnChildClickListener childListener = new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
Intent intent = new Intent(JobsInformationActivity.this, JobsListActivity.class);
intent.putExtra(GlobalKeys.CATEGORY_ID, id);
startActivity(intent);
return false;
}
};
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setView(R.layout.um_information_activity);
setTitle(getString(R.string.job_info_main));
setTitleBar(R.drawable.home_back, "", 0, "");
// mToolBar = (ToolBar) findViewById(R.id.tool_bar);
// mToolBar.setSelection(3);
latest = (Button) findViewById(R.id.latest);
fullTime = (Button) findViewById(R.id.fulltime);
partTime = (Button) findViewById(R.id.parttime);
fullTime.setOnClickListener(this);
partTime.setOnClickListener(this);
latest.setOnClickListener(this);
latest.requestFocus();
mPullDownView = (PullDownView) findViewById(R.id.latest_list);
mPullDownView.setOnPullDownListener(this);
mListView = mPullDownView.getListView();
jobListAdapter = new JobsListAdapter(this);
mListView.setAdapter(jobListAdapter);
mListView.setOnItemClickListener(this);
mPullDownView.enableAutoFetchMore(false, 1);
catelogL = (LinearLayout) findViewById(R.id.category_layout);
exListV = (ExpandableListView) findViewById(R.id.categoryList);
exListV.setGroupIndicator(null);
loadingL = (RelativeLayout) findViewById(R.id.loading);
mAdapter = new CategoryExpListAdapter(this);
exListV.setAdapter(mAdapter);
exListV.setOnChildClickListener(childListener);
searchBtn = (Button) findViewById(R.id.search);
searchInput = (EditText) findViewById(R.id.search_input);
searchBtn.setOnClickListener(this);
mManager = new CategoryManager();
jobManager = new JobsManager();
HandlerManager.handlerMap.put(HandlerManager.KEY_CATEGORY, mHandler);
// mManager.getCategoryList(1);
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
jobManager.getLatestJobs(lastRecTime);
}
@Override
public void handleClick(int buttonId) {
switch (buttonId) {
case R.id.latest:
if (tabIndex == TabIndex.LATEST) {
return;
}
//设置背景色
latest.setBackgroundResource(R.drawable.zuixin2);
fullTime.setBackgroundResource(R.drawable.quanzhi2);
partTime.setBackgroundResource(R.drawable.jianzhi2);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj1);
if (mPullDownView.getVisibility() == View.GONE) {
exListV.setVisibility(View.GONE);
mPullDownView.setVisibility(View.VISIBLE);
}
tabIndex = TabIndex.LATEST;
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
// mManager.getCategoryList(1);
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.fulltime:
if (tabIndex == TabIndex.FULL_TIME) {
return;
}
//设置背景色
latest.setBackgroundResource(R.drawable.zuixin1);
fullTime.setBackgroundResource(R.drawable.quanzhi1);
partTime.setBackgroundResource(R.drawable.jianzhi2);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj2);
if (fullTimeGList == null) {
loadingL.setVisibility(View.VISIBLE);
exListV.setVisibility(View.GONE);
mManager.getCategoryList(1);
} else {
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
mAdapter.setData(fullTimeGList);
}
if (tabIndex == TabIndex.LATEST) {
exListV.setVisibility(View.VISIBLE);
mPullDownView.setVisibility(View.GONE);
}
tabIndex = TabIndex.FULL_TIME;
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.parttime:
if (tabIndex == TabIndex.PART_TIME) {
return;
}
latest.setBackgroundResource(R.drawable.zuixin1);
fullTime.setBackgroundResource(R.drawable.quanzhi2);
partTime.setBackgroundResource(R.drawable.jianzhi1);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj3);
if (partTimeGList == null) {
loadingL.setVisibility(View.VISIBLE);
exListV.setVisibility(View.GONE);
mManager.getCategoryList(2);
} else {
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
mAdapter.setData(partTimeGList);
}
if (tabIndex == TabIndex.LATEST) {
exListV.setVisibility(View.VISIBLE);
mPullDownView.setVisibility(View.GONE);
}
tabIndex = TabIndex.PART_TIME;
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.search:
final String keyword = searchInput.getText().toString();
if (!TextUtils.isEmpty(keyword)) {
Intent intent = new Intent(Constants.ACTION_SEARCH);
intent.putExtra(GlobalKeys.KEYWORD, keyword);
startActivity(intent);
}
break;
}
}
public void onMessage(Message msg) {
switch (msg.what) {
case LatestJobsRespProcessor.MSG_REFRESH_JOBS:
List<JobInfo> list = (List<JobInfo>)msg.obj;
boolean isFirstLoad = "".equals(lastRecTime);
if (list.size() > 0) {
lastRecTime = list.get(0).date;
jobListAdapter.addDataToFront(list);
}
if (isFirstLoad) {
mPullDownView.notifyDidLoad();
} else {
mPullDownView.notifyDidRefresh();
}
break;
case CateListRespProcessor.MSG_REFRESH:
List<CategoryItem> itemList = (List<CategoryItem>)msg.obj;
mAdapter.setData(itemList);
if (tabIndex == TabIndex.FULL_TIME) {
fullTimeGList = itemList;
} else if (tabIndex == TabIndex.PART_TIME) {
partTimeGList = itemList;
}
if (mProgressDialog != null) {
mProgressDialog.cancel();
mProgressDialog = null;
}
if (mPullDownView.getVisibility() == View.VISIBLE) {
mPullDownView.setVisibility(View.GONE);
}
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
break;
}
}
@Override
protected void onLeftAction() {
finish();
}
@Override
protected void onRightAction() {
}
@Override
public void onMore() {
mPullDownView.notifyDidMore();
}
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
Intent intent = new Intent(this, JobDetailActivity.class);
intent.putExtra("id", (int)id);
JobInfo info = (JobInfo) adapter.getItemAtPosition(position);
intent.putExtra("title", info.title);
intent.putExtra("number", info.num);
startActivity(intent);
}
@Override
public void onRefresh() {
jobManager.getLatestJobs(lastRecTime);
mListView.setSelection(-1);
}
}
其中onRefresh异步调用网络数据, 在onMessage中返回, 更新列表数据。
至此结束,希望对大家有帮助。
首先要明确一点的是,ListView提供了HeaderView 和FooterView用于增加顶部和底部视图。 头部的刷新调和底部的更多条目都是使用这两个来实现的。
下面我们来看看怎么实现的:
1. 对ListView进行集成,主要是监听滑动事件,触发底端和顶端事件:
package com.drawthink.nxjobhunting.ui.components;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
/**
* <p>一个可以监听ListView是否滚动到最顶部或最底部的自定义控件</p>
* 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br>
* 如果加以改进,可以实现监听scroll滚动的具体位置等
* @author Seya Qu</br> Email:seyason@gmail.com
*/
public class ScrollOverListView extends ListView {
private int mLastY;
private int mTopPosition;
private int mBottomPosition;
public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public ScrollOverListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ScrollOverListView(Context context) {
super(context);
init();
}
private void init(){
mTopPosition = 0;
mBottomPosition = 0;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final int y = (int) ev.getRawY();
switch(action){
case MotionEvent.ACTION_DOWN:{
mLastY = y;
final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);
if (isHandled) {
mLastY = y;
return isHandled;
}
break;
}
case MotionEvent.ACTION_MOVE:{
final int childCount = getChildCount();
if(childCount == 0) return super.onTouchEvent(ev);
final int itemCount = getAdapter().getCount() - mBottomPosition;
final int deltaY = y - mLastY;
//DLog.d("lastY=%d y=%d", mLastY, y);
final int firstTop = getChildAt(0).getTop();
final int listPadding = getListPaddingTop();
final int lastBottom = getChildAt(childCount - 1).getBottom();
final int end = getHeight() - getPaddingBottom();
final int firstVisiblePosition = getFirstVisiblePosition();
final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY);
if(isHandleMotionMove){
mLastY = y;
return true;
}
//DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);
if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {
final boolean isHandleOnListViewTopAndPullDown;
isHandleOnListViewTopAndPullDown = mOnScrollOverListener.onListViewTopAndPullDown(deltaY);
if(isHandleOnListViewTopAndPullDown){
mLastY = y;
return true;
}
}
// DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);
if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {
final boolean isHandleOnListViewBottomAndPullDown;
isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY);
if(isHandleOnListViewBottomAndPullDown){
mLastY = y;
return true;
}
}
break;
}
case MotionEvent.ACTION_UP:{
final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev);
if (isHandlerMotionUp) {
mLastY = y;
return true;
}
break;
}
}
mLastY = y;
return super.onTouchEvent(ev);
}
/**空的*/
private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){
@Override
public boolean onListViewTopAndPullDown(int delta) {
return false;
}
@Override
public boolean onListViewBottomAndPullUp(int delta) {
return false;
}
@Override
public boolean onMotionDown(MotionEvent ev) {
return false;
}
@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
return false;
}
@Override
public boolean onMotionUp(MotionEvent ev) {
return false;
}
};
// =============================== public method ===============================
/**
* 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个
*
* @param index 正数第几个,必须在条目数范围之内
*/
public void setTopPosition(int index){
if(getAdapter() == null)
throw new NullPointerException("You must set adapter before setTopPosition!");
if(index < 0)
throw new IllegalArgumentException("Top position must > 0");
mTopPosition = index;
}
/**
* 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个
*
* @param index 倒数第几个,必须在条目数范围之内
*/
public void setBottomPosition(int index){
if(getAdapter() == null)
throw new NullPointerException("You must set adapter before setBottonPosition!");
if(index < 0)
throw new IllegalArgumentException("Bottom position must > 0");
mBottomPosition = index;
}
/**
* 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br>
*
* @see OnScrollOverListener
*/
public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener){
mOnScrollOverListener = onScrollOverListener;
}
/**
* 滚动监听接口</br>
* @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)
*
* @author Seya Qu</br> Email:seyason@gmail.com
*/
public interface OnScrollOverListener {
/**
* 到达最顶部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewTopAndPullDown(int delta);
/**
* 到达最底部触发
*
* @param delta 手指点击移动产生的偏移量
* @return
*/
boolean onListViewBottomAndPullUp(int delta);
/**
* 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionDown(MotionEvent ev);
/**
* 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionMove(MotionEvent ev, int delta);
/**
* 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}
*
* @return 返回true表示自己处理
* @see View#onTouchEvent(MotionEvent)
*/
boolean onMotionUp(MotionEvent ev);
}
}
2. 定义PullDownView, 继承LinearLayout, 主要是为了调用和监听ListView, 通过接口OnPullDownListener的onRefresh来通知外部进行顶部数据的刷新, onMore触发请求底部更多数据, 具体如何去获取数据, 需要在外面的Activity中实现。
package com.drawthink.nxjobhunting.ui.components;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import com.drawthink.nxjobhunting.R;
import com.drawthink.nxjobhunting.ui.components.ScrollOverListView.OnScrollOverListener;
import com.drawthink.nxjobhunting.utils.GlobalVar;
import com.drawthink.nxjobhunting.utils.LogX;
/**
* 下拉刷新控件</br>
* 真正实现下拉刷新的是这个控件,
* ScrollOverListView只是提供触摸的事件等
* @author Seya Qu</br> Email:seyason@gmail.com
*/
public class PullDownView extends LinearLayout implements OnScrollOverListener{
private static final int START_PULL_DEVIATION = 50; // 移动误差
private static final int AUTO_INCREMENTAL = 10; // 自增量,用于回弹
private static final int WHAT_DID_LOAD_DATA = 1; // Handler what 数据加载完毕
private static final int WHAT_ON_REFRESH = 2; // Handler what 刷新中
private static final int WHAT_DID_REFRESH = 3; // Handler what 已经刷新完
private static final int WHAT_SET_HEADER_HEIGHT = 4;// Handler what 设置高度
private static final int WHAT_DID_MORE = 5; // Handler what 已经获取完更多
private static final int DEFAULT_HEADER_VIEW_HEIGHT = 105; // 头部文件原本的高度
private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm");
private View mHeaderView;
private LayoutParams mHeaderViewParams;
private TextView mHeaderViewDateView;
private TextView mHeaderTextView;
private ImageView mHeaderArrowView;
private View mHeaderLoadingView;
private View mFooterView;
private TextView mFooterTextView;
private View mFooterLoadingView;
private ScrollOverListView mListView;
private OnPullDownListener mOnPullDownListener;
private RotateAnimation mRotateOTo180Animation;
private RotateAnimation mRotate180To0Animation;
private int mHeaderIncremental; // 增量
private float mMotionDownLastY; // 按下时候的Y轴坐标
private boolean mIsDown; // 是否按下
private boolean mIsRefreshing; // 是否下拉刷新中
private boolean mIsFetchMoreing; // 是否获取更多中
private boolean mIsPullUpDone; // 是否回推完成
private boolean mEnableAutoFetchMore; // 是否允许自动获取更多
// 头部文件的状态
private static final int HEADER_VIEW_STATE_IDLE = 0; // 空闲
private static final int HEADER_VIEW_STATE_NOT_OVER_HEIGHT = 1; // 没有超过默认高度
private static final int HEADER_VIEW_STATE_OVER_HEIGHT = 2; // 超过默认高度
private int mHeaderViewState = HEADER_VIEW_STATE_IDLE;
public PullDownView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeaderViewAndFooterViewAndListView(context);
}
public PullDownView(Context context) {
super(context);
initHeaderViewAndFooterViewAndListView(context);
}
/*
* ==================================
* Public method
* 外部使用,具体就是用这几个就可以了
*
* ==================================
*/
/**
* 刷新事件接口
* @author 何龙威 Email:kjsoloho@gmail.com
*/
public interface OnPullDownListener {
void onRefresh();
void onMore();
}
/**
* 通知加载完了数据,要放在Adapter.notifyDataSetChanged后面
* 当你加载完数据的时候,调用这个notifyDidLoad()
* 才会隐藏头部,并初始化数据等
*/
public void notifyDidLoad() {
mUIHandler.sendEmptyMessage(WHAT_DID_LOAD_DATA);
}
/**
* 通知已经刷新完了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完刷新任务之后,调用这个notifyDidRefresh()
* 才会隐藏掉头部文件等操作
*/
public void notifyDidRefresh() {
mUIHandler.sendEmptyMessage(WHAT_DID_REFRESH);
}
/**
* 通知已经获取完更多了,要放在Adapter.notifyDataSetChanged后面
* 当你执行完更多任务之后,调用这个notyfyDidMore()
* 才会隐藏加载圈等操作
*/
public void notifyDidMore() {
mUIHandler.sendEmptyMessage(WHAT_DID_MORE);
}
/**
* 设置监听器
* @param listener
*/
public void setOnPullDownListener(OnPullDownListener listener){
mOnPullDownListener = listener;
}
/**
* 获取内嵌的listview
* @return ScrollOverListView
*/
public ListView getListView(){
return mListView;
}
/**
* 是否开启自动获取更多
* 自动获取更多,将会隐藏footer,并在到达底部的时候自动刷新
* @param index 倒数第几个触发
*/
public void enableAutoFetchMore(boolean enable, int index){
if(enable){
mListView.setBottomPosition(index);
mFooterLoadingView.setVisibility(View.VISIBLE);
}else{
mFooterTextView.setText("更多");
mFooterLoadingView.setVisibility(View.GONE);
}
mEnableAutoFetchMore = enable;
}
/*
* ==================================
* Private method
* 具体实现下拉刷新等操作
*
* ==================================
*/
/**
* 初始化界面
*/
private void initHeaderViewAndFooterViewAndListView(Context context){
setOrientation(LinearLayout.VERTICAL);
//setDrawingCacheEnabled(false);
/*
* 自定义头部文件
* 放在这里是因为考虑到很多界面都需要使用
* 如果要修改,和它相关的设置都要更改
*/
mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);
mHeaderViewParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
addView(mHeaderView, 0, mHeaderViewParams);
mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);
mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);
mHeaderLoadingView = mHeaderView.findViewById(R.id.pulldown_header_loading);
// 注意,图片旋转之后,再执行旋转,坐标会重新开始计算
mRotateOTo180Animation = new RotateAnimation(0, 180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotateOTo180Animation.setDuration(250);
mRotateOTo180Animation.setFillAfter(true);
mRotate180To0Animation = new RotateAnimation(180, 0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotate180To0Animation.setDuration(250);
mRotate180To0Animation.setFillAfter(true);
/**
* 自定义脚部文件
*/
mFooterView = LayoutInflater.from(context).inflate(R.layout.pulldown_footer, null);
mFooterTextView = (TextView) mFooterView.findViewById(R.id.pulldown_footer_text);
mFooterLoadingView = mFooterView.findViewById(R.id.pulldown_footer_loading);
mFooterView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(!mIsFetchMoreing){
mIsFetchMoreing = true;
mFooterTextView.setText("加载更多中...");
mFooterLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onMore();
}
}
});
/*
* ScrollOverListView 同样是考虑到都是使用,所以放在这里
* 同时因为,需要它的监听事件
*/
mListView = new ScrollOverListView(context);
mListView.setVerticalScrollBarEnabled(false);
mListView.setOnScrollOverListener(this);
mListView.setCacheColorHint(0);
mListView.setDivider(GlobalVar.appContext.getResources().getDrawable(R.drawable.dot));
addView(mListView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
// 空的listener
mOnPullDownListener = new OnPullDownListener() {
@Override
public void onRefresh() {}
@Override
public void onMore() {}
};
}
/**
* 在下拉和回推的时候检查头部文件的状态</br>
* 如果超过了默认高度,就显示松开可以刷新,
* 否则显示下拉可以刷新
*/
private void checkHeaderViewState(){
if(mHeaderViewParams.height >= DEFAULT_HEADER_VIEW_HEIGHT){
if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;
mHeaderViewState = HEADER_VIEW_STATE_OVER_HEIGHT;
mHeaderTextView.setText("松开可以刷新");
mHeaderArrowView.startAnimation(mRotateOTo180Animation);
}else{
if(mHeaderViewState == HEADER_VIEW_STATE_NOT_OVER_HEIGHT
|| mHeaderViewState == HEADER_VIEW_STATE_IDLE) return;
mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;
mHeaderTextView.setText("下拉可以刷新");
mHeaderArrowView.startAnimation(mRotate180To0Animation);
}
}
private void setHeaderHeight(final int height){
mHeaderIncremental = height;
mHeaderViewParams.height = height;
mHeaderView.setLayoutParams(mHeaderViewParams);
}
/**
* 自动隐藏动画
* @author 何龙威 Email:kjsoloho@gmail.com
*/
class HideHeaderViewTask extends TimerTask{
@Override
public void run() {
if(mIsDown) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > 0){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = 0;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
cancel();
}
}
}
/**
* 自动显示动画
* @author 何龙威 Email:kjsoloho@gmail.com
*/
class ShowHeaderViewTask extends TimerTask{
@Override
public void run() {
if(mIsDown) {
cancel();
return;
}
mHeaderIncremental -= AUTO_INCREMENTAL;
if(mHeaderIncremental > DEFAULT_HEADER_VIEW_HEIGHT){
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
}else{
mHeaderIncremental = DEFAULT_HEADER_VIEW_HEIGHT;
mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
if(!mIsRefreshing){
mIsRefreshing = true;
mUIHandler.sendEmptyMessage(WHAT_ON_REFRESH);
}
cancel();
}
}
}
private Handler mUIHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case WHAT_DID_LOAD_DATA:{
mHeaderViewParams.height = 0;
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderTextView.setText("下拉可以刷新");
mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);
mHeaderViewDateView.setVisibility(View.VISIBLE);
mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
mHeaderArrowView.setVisibility(View.VISIBLE);
showFooterView();
return;
}
case WHAT_ON_REFRESH:{
// 要清除掉动画,否则无法隐藏
mHeaderArrowView.clearAnimation();
mHeaderArrowView.setVisibility(View.INVISIBLE);
mHeaderLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onRefresh();
return;
}
case WHAT_DID_REFRESH :{
mIsRefreshing = false;
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
mHeaderArrowView.setVisibility(View.VISIBLE);
mHeaderLoadingView.setVisibility(View.GONE);
mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
setHeaderHeight(0);
showFooterView();
return;
}
case WHAT_SET_HEADER_HEIGHT :{
setHeaderHeight(mHeaderIncremental);
return;
}
case WHAT_DID_MORE :{
mIsFetchMoreing = false;
mFooterTextView.setText("更多");
mFooterLoadingView.setVisibility(View.GONE);
}
}
}
};
/**
* 显示脚步脚部文件
*/
private void showFooterView(){
if(mListView.getFooterViewsCount() == 0 && isFillScreenItem()){
mListView.addFooterView(mFooterView);
mListView.setAdapter(mListView.getAdapter());
}
}
/**
* 条目是否填满整个屏幕
*/
private boolean isFillScreenItem(){
final int firstVisiblePosition = mListView.getFirstVisiblePosition();
final int lastVisiblePostion = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();
final int visibleItemCount = lastVisiblePostion - firstVisiblePosition + 1;
final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();
if(visibleItemCount < totalItemCount) return true;
return false;
}
/*
* ==================================
* 实现 OnScrollOverListener接口
*
*
* ==================================
*/
@Override
public boolean onListViewTopAndPullDown(int delta) {
if(mIsRefreshing || mListView.getCount() - mListView.getFooterViewsCount() == 0) return false;
int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);
mHeaderIncremental += i;
if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight
setHeaderHeight(mHeaderIncremental);
checkHeaderViewState();
}
return true;
}
@Override
public boolean onListViewBottomAndPullUp(int delta) {
if(!mEnableAutoFetchMore || mIsFetchMoreing) return false;
// 数量充满屏幕才触发
if(isFillScreenItem()){
mIsFetchMoreing = true;
mFooterTextView.setText("加载更多中...");
mFooterLoadingView.setVisibility(View.VISIBLE);
mOnPullDownListener.onMore();
return true;
}
return false;
}
@Override
public boolean onMotionDown(MotionEvent ev) {
mIsDown = true;
mIsPullUpDone = false;
mMotionDownLastY = ev.getRawY();
return false;
}
@Override
public boolean onMotionMove(MotionEvent ev, int delta) {
//当头部文件回推消失的时候,不允许滚动
if(mIsPullUpDone) return true;
// 如果开始按下到滑动距离不超过误差值,则不滑动
final int absMotionY = (int) Math.abs(ev.getRawY() - mMotionDownLastY);
if(absMotionY < START_PULL_DEVIATION) return true;
final int absDelta = Math.abs(delta);
final int i = (int) Math.ceil((double)absDelta / 2);
// onTopDown在顶部,并上回推和onTopUp相对
if(mHeaderViewParams.height > 0 && delta < 0){
mHeaderIncremental -= i;
if(mHeaderIncremental > 0){
setHeaderHeight(mHeaderIncremental);
checkHeaderViewState();
}else{
mHeaderViewState = HEADER_VIEW_STATE_IDLE;
mHeaderIncremental = 0;
setHeaderHeight(mHeaderIncremental);
mIsPullUpDone = true;
}
return true;
}
return false;
}
@Override
public boolean onMotionUp(MotionEvent ev) {
mIsDown = false;
// 避免和点击事件冲突
if(mHeaderViewParams.height > 0){
// 判断头文件拉动的距离与设定的高度,小了就隐藏,多了就固定高度
int x = mHeaderIncremental - DEFAULT_HEADER_VIEW_HEIGHT;
Timer timer = new Timer(true);
if(x < 0){
timer.scheduleAtFixedRate(new HideHeaderViewTask(), 0, 10);
}else{
timer.scheduleAtFixedRate(new ShowHeaderViewTask(), 0, 10);
}
return true;
}
return false;
}
}
3. xml布局文件Layout中的定义
pulldown_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingTop="10dp" >
<TextView
android:id="@+id/pulldown_footer_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="更多"
android:textSize="15dp" />
<ProgressBar
android:id="@+id/pulldown_footer_loading"
style="@android:style/Widget.ProgressBar.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:visibility="gone" />
</RelativeLayout>
pulldown_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingTop="10dp" >
<ImageView
android:id="@+id/pulldown_header_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="20dp"
android:scaleType="centerCrop"
android:src="@drawable/z_arrow_down"
android:visibility="invisible" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/pulldown_header_arrow"
android:layout_alignTop="@+id/pulldown_header_arrow"
android:layout_centerHorizontal="true"
android:gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/pulldown_header_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="加载中..." />
<TextView
android:id="@+id/pulldown_header_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="更新于:"
android:visibility="gone" />
</LinearLayout>
<ProgressBar
android:id="@+id/pulldown_header_loading"
style="@android:style/Widget.ProgressBar.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp" />
</RelativeLayout>
调用自定义的PullDownView部分布局文件
um_information_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:background="#FFFFFF" android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
...
<com.drawthink.nxjobhunting.ui.components.PullDownView
android:id="@+id/latest_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="@android:color/white"
android:layout_below="@id/category_layout"
>
</com.drawthink.nxjobhunting.ui.components.PullDownView>
</RelativeLayout>
4. Activity文件
JobsInformationActivity
package com.drawthink.nxjobhunting.ui;
import java.util.ArrayList;
import java.util.List;
import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import com.drawthink.nxjobhunting.R;
import com.drawthink.nxjobhunting.handler.HandlerManager;
import com.drawthink.nxjobhunting.http.response.CateListRespProcessor;
import com.drawthink.nxjobhunting.http.response.LatestJobsRespProcessor;
import com.drawthink.nxjobhunting.logic.CategoryManager;
import com.drawthink.nxjobhunting.logic.JobsManager;
import com.drawthink.nxjobhunting.ui.adapter.CategoryExpListAdapter;
import com.drawthink.nxjobhunting.ui.adapter.JobsListAdapter;
import com.drawthink.nxjobhunting.ui.components.PullDownView;
import com.drawthink.nxjobhunting.ui.components.PullDownView.OnPullDownListener;
import com.drawthink.nxjobhunting.utils.Constants;
import com.drawthink.nxjobhunting.utils.GlobalKeys;
import com.drawthink.nxjobhunting.vo.CategoryItem;
import com.drawthink.nxjobhunting.vo.JobInfo;
public class JobsInformationActivity extends BaseActivity implements OnPullDownListener, OnItemClickListener{
public static enum TabIndex{
LATEST,
FULL_TIME,
PART_TIME
}
private CategoryManager mManager;
private JobsManager jobManager;
// private ToolBar mToolBar;
private Button latest;
private Button fullTime;
private Button partTime;
private CategoryExpListAdapter mAdapter;
private ExpandableListView exListV;
private Button searchBtn;
private EditText searchInput;
private LinearLayout catelogL;
private RelativeLayout loadingL;
//最新职位列表
private PullDownView mPullDownView;
private ListView mListView;
private JobsListAdapter jobListAdapter;
private List<JobInfo> listData;
private TabIndex tabIndex = TabIndex.LATEST;
private String lastRecTime = "";
//keep category list in memory
public static List<CategoryItem> fullTimeGList = null;
public static List<CategoryItem> partTimeGList = null;
private OnChildClickListener childListener = new OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
Intent intent = new Intent(JobsInformationActivity.this, JobsListActivity.class);
intent.putExtra(GlobalKeys.CATEGORY_ID, id);
startActivity(intent);
return false;
}
};
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setView(R.layout.um_information_activity);
setTitle(getString(R.string.job_info_main));
setTitleBar(R.drawable.home_back, "", 0, "");
// mToolBar = (ToolBar) findViewById(R.id.tool_bar);
// mToolBar.setSelection(3);
latest = (Button) findViewById(R.id.latest);
fullTime = (Button) findViewById(R.id.fulltime);
partTime = (Button) findViewById(R.id.parttime);
fullTime.setOnClickListener(this);
partTime.setOnClickListener(this);
latest.setOnClickListener(this);
latest.requestFocus();
mPullDownView = (PullDownView) findViewById(R.id.latest_list);
mPullDownView.setOnPullDownListener(this);
mListView = mPullDownView.getListView();
jobListAdapter = new JobsListAdapter(this);
mListView.setAdapter(jobListAdapter);
mListView.setOnItemClickListener(this);
mPullDownView.enableAutoFetchMore(false, 1);
catelogL = (LinearLayout) findViewById(R.id.category_layout);
exListV = (ExpandableListView) findViewById(R.id.categoryList);
exListV.setGroupIndicator(null);
loadingL = (RelativeLayout) findViewById(R.id.loading);
mAdapter = new CategoryExpListAdapter(this);
exListV.setAdapter(mAdapter);
exListV.setOnChildClickListener(childListener);
searchBtn = (Button) findViewById(R.id.search);
searchInput = (EditText) findViewById(R.id.search_input);
searchBtn.setOnClickListener(this);
mManager = new CategoryManager();
jobManager = new JobsManager();
HandlerManager.handlerMap.put(HandlerManager.KEY_CATEGORY, mHandler);
// mManager.getCategoryList(1);
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
jobManager.getLatestJobs(lastRecTime);
}
@Override
public void handleClick(int buttonId) {
switch (buttonId) {
case R.id.latest:
if (tabIndex == TabIndex.LATEST) {
return;
}
//设置背景色
latest.setBackgroundResource(R.drawable.zuixin2);
fullTime.setBackgroundResource(R.drawable.quanzhi2);
partTime.setBackgroundResource(R.drawable.jianzhi2);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj1);
if (mPullDownView.getVisibility() == View.GONE) {
exListV.setVisibility(View.GONE);
mPullDownView.setVisibility(View.VISIBLE);
}
tabIndex = TabIndex.LATEST;
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
// mManager.getCategoryList(1);
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.fulltime:
if (tabIndex == TabIndex.FULL_TIME) {
return;
}
//设置背景色
latest.setBackgroundResource(R.drawable.zuixin1);
fullTime.setBackgroundResource(R.drawable.quanzhi1);
partTime.setBackgroundResource(R.drawable.jianzhi2);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj2);
if (fullTimeGList == null) {
loadingL.setVisibility(View.VISIBLE);
exListV.setVisibility(View.GONE);
mManager.getCategoryList(1);
} else {
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
mAdapter.setData(fullTimeGList);
}
if (tabIndex == TabIndex.LATEST) {
exListV.setVisibility(View.VISIBLE);
mPullDownView.setVisibility(View.GONE);
}
tabIndex = TabIndex.FULL_TIME;
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.parttime:
if (tabIndex == TabIndex.PART_TIME) {
return;
}
latest.setBackgroundResource(R.drawable.zuixin1);
fullTime.setBackgroundResource(R.drawable.quanzhi2);
partTime.setBackgroundResource(R.drawable.jianzhi1);
catelogL.setBackgroundResource(R.drawable.zhiwei_bj3);
if (partTimeGList == null) {
loadingL.setVisibility(View.VISIBLE);
exListV.setVisibility(View.GONE);
mManager.getCategoryList(2);
} else {
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
mAdapter.setData(partTimeGList);
}
if (tabIndex == TabIndex.LATEST) {
exListV.setVisibility(View.VISIBLE);
mPullDownView.setVisibility(View.GONE);
}
tabIndex = TabIndex.PART_TIME;
// if(mProgressDialog == null) {
// mProgressDialog = ProgressDialog.show(this, "", "正在加载,请稍后...");
// }
break;
case R.id.search:
final String keyword = searchInput.getText().toString();
if (!TextUtils.isEmpty(keyword)) {
Intent intent = new Intent(Constants.ACTION_SEARCH);
intent.putExtra(GlobalKeys.KEYWORD, keyword);
startActivity(intent);
}
break;
}
}
public void onMessage(Message msg) {
switch (msg.what) {
case LatestJobsRespProcessor.MSG_REFRESH_JOBS:
List<JobInfo> list = (List<JobInfo>)msg.obj;
boolean isFirstLoad = "".equals(lastRecTime);
if (list.size() > 0) {
lastRecTime = list.get(0).date;
jobListAdapter.addDataToFront(list);
}
if (isFirstLoad) {
mPullDownView.notifyDidLoad();
} else {
mPullDownView.notifyDidRefresh();
}
break;
case CateListRespProcessor.MSG_REFRESH:
List<CategoryItem> itemList = (List<CategoryItem>)msg.obj;
mAdapter.setData(itemList);
if (tabIndex == TabIndex.FULL_TIME) {
fullTimeGList = itemList;
} else if (tabIndex == TabIndex.PART_TIME) {
partTimeGList = itemList;
}
if (mProgressDialog != null) {
mProgressDialog.cancel();
mProgressDialog = null;
}
if (mPullDownView.getVisibility() == View.VISIBLE) {
mPullDownView.setVisibility(View.GONE);
}
if (loadingL.getVisibility() == View.VISIBLE) {
loadingL.setVisibility(View.GONE);
}
exListV.setVisibility(View.VISIBLE);
break;
}
}
@Override
protected void onLeftAction() {
finish();
}
@Override
protected void onRightAction() {
}
@Override
public void onMore() {
mPullDownView.notifyDidMore();
}
@Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
Intent intent = new Intent(this, JobDetailActivity.class);
intent.putExtra("id", (int)id);
JobInfo info = (JobInfo) adapter.getItemAtPosition(position);
intent.putExtra("title", info.title);
intent.putExtra("number", info.num);
startActivity(intent);
}
@Override
public void onRefresh() {
jobManager.getLatestJobs(lastRecTime);
mListView.setSelection(-1);
}
}
其中onRefresh异步调用网络数据, 在onMessage中返回, 更新列表数据。
至此结束,希望对大家有帮助。
发表评论
-
基于Java NIO的手机答题游戏开发
2014-03-31 17:59 1785先上个游戏截图: 豌豆荚地址: http://apps.w ... -
enable android deviceconnection under linux
2013-04-24 15:45 1004开发环境搭建完毕,Eclipse,Java,ADT都已经折腾 ... -
set CCache for building android source code
2013-04-24 15:14 1191Setting up ccache You can opti ... -
android 源码下载403 forbidden, 406 not acceptable 错误
2013-04-24 08:46 2453解决方法 1. 浏览器登录https://android.g ... -
ubuntu11.10 编译android4.0 错处
2013-04-08 21:45 1226今天使用ubuntu11.10编译android4.0出现以下 ... -
常用WebService列表
2013-01-31 23:22 2638快递查询接口 http://webservice.36wu.c ... -
startActivityForResult 无响应问题
2013-01-24 13:57 3853今天开发遇到了个问题, 就是从一个Activity中使用sta ... -
Grid调整间距
2012-09-29 08:52 1747今天使用GridView, 发现点中某一Item时选中部分的背 ... -
android gallery和Animation组合使用, 看美女美图
2012-09-11 21:01 4450今天主要探究Gallery和Animation的使用。 制作一 ... -
Android执行 shell command
2012-03-24 17:00 7255Android执行shell命令 一、方法 1. /* ... -
BBBBBB111
2012-03-23 11:42 10陈波: 本周:代码review,解决findbugs中bug以 ... -
MonkeyTestError
2012-03-21 10:15 903-20 21:36:42.439 W/dalvikvm( ... -
INSTALL_FAILED_OLDER_SDK ERROR
2012-03-12 15:52 4471Install APK with adb: $ platfo ... -
4.0源码编译问题
2012-03-07 17:12 31581、fatal error: GL/glx.h: No suc ... -
4.0 编译apk中无classes.dex
2012-03-07 17:11 3964下载完android 4.0代码,模拟器里面的gallery不 ... -
Android 4.0源码编译错误
2012-03-05 10:19 2582UNEXPECTED TOP-LEVEL EXCEPTION: ... -
AndroidLockScreenDemo
2012-02-18 15:54 993锁屏解锁的成功案例。 -
G14 root权限获取
2012-02-13 23:36 2869HTC G14 ROOT权限获取后就能删除系统自带的程序,相信 ... -
锁屏d ds
2011-12-15 00:49 903private final IDevicePolicyMana ... -
HuHug
2011-11-14 00:30 959gfjijgsdklgkg
相关推荐
Android-仿新浪微博下拉刷新继承FrameLayout(源码).zip
本资源“Android应用源码仿新浪微博下拉刷新继承FrameLayout.zip”提供了一个实现这一功能的源码示例,适用于Android开发者进行学习和参考。此示例通过继承`FrameLayout`来构建自定义的下拉刷新组件,模仿了新浪微博...
这篇文档将深入解析《Android应用源码仿新浪微博下拉刷新继承FrameLayout》这一主题,它是一个Android开发的Demo,特别适合于毕业设计学习。在Android应用程序开发中,下拉刷新功能已经成为用户界面的一个常见且重要...
这个压缩包"安卓Android源码——仿新浪微博下拉刷新继承FrameLayout.rar"提供了一个示例,它演示了如何在自定义视图中实现这一功能,特别是通过继承`FrameLayout`来完成。下面将详细解释相关知识点。 1. **自定义...
在Android平台上,构建一个仿新浪微博手机客户端界面是一个挑战性的任务,涉及到多个技术点和设计原则。这个项目的主要目标是提供用户一个与微博应用相似的体验,包括底部导航栏,美观的界面设计,以及可能的交互...
在Android开发中,"基于Android的仿新浪微博下拉刷新继承FrameLayout.zip" 是一个实践项目,旨在教授如何在Android应用中实现类似新浪微博的下拉刷新功能。这个项目的核心是通过自定义View组件来扩展Android的基础...
本项目“Android项目仿新浪微博下拉刷新继承FrameLayout”旨在实现一个类似新浪微博的下拉刷新效果,通过自定义View组件,将刷新功能集成到一个继承自FrameLayout的基础布局中。下面我们将详细讨论这个项目中的关键...
在Android应用开发中,"仿新浪微博下拉刷新"是一种常见的用户体验设计,用于更新列表数据。这一功能让用户在列表顶部向下拉动时触发加载新内容的机制,常见于新闻、社交网络等应用。`PullToRefresh`库是实现这一功能...
本项目主题是“android仿新浪微博下拉刷新继承FrameLayout”,这表明我们将讨论如何在Android应用中创建一个自定义的下拉刷新组件,该组件基于`FrameLayout`进行扩展。`FrameLayout`是Android的基础布局之一,它允许...
这个压缩包文件“安卓Android源码——仿新浪微博下拉刷新继承FrameLayout.zip”提供了一个实现这一功能的源代码示例,它基于Android框架的`FrameLayout`组件进行扩展。下面将详细介绍这个示例中的核心知识点。 1. *...
【Android 仿新浪微博】项目是基于Android平台的一个开源项目,其目标是实现一个类似新浪微博的应用。这个项目对于学习Android开发,尤其是社交应用的构建,提供了丰富的实践参考。 首先,项目的核心在于用户界面...
在Android平台上,开发一款仿新浪微博客户端是一项挑战性的任务,它涉及到多个关键的技术点,包括UI设计、网络请求、数据解析、缓存策略、用户登录授权、动态加载与刷新、社交功能实现等。以下是对这个项目中涉及的...
Android应用源码仿新浪微博下拉刷新继承FrameLayout.zip项目安卓应用源码下载Android应用源码仿新浪微博下拉刷新继承FrameLayout.zip项目安卓应用源码下载 1.适合学生毕业设计研究参考 2.适合个人学习研究参考 3....
本教程将详细讲解如何实现一个类似新浪微博的下拉刷新列表,采用`FrameLayout`布局作为基础。 首先,我们要理解`FrameLayout`。`FrameLayout`是Android中的一个布局容器,它按照从上到下的顺序堆叠子视图。当多个...
这个功能在微博应用中尤为常见,如新浪微博和QQ微博,用户只需在顶部向下拉动列表,当达到一定距离时松手,即可触发数据刷新。在本教程中,我们将深入探讨如何在Android应用中实现类似的功能。 一、下拉刷新组件...
在Android应用开发中,"android activity选项卡 listview 下拉刷新 仿新浪微博"是一个常见的功能需求,它涉及到多个关键知识点,包括Activity管理、ListView的使用、下拉刷新(Pull-to-Refresh)机制以及模仿知名...
本文将详细介绍如何在Android应用中集成新浪微博分享功能,包括授权认证和内容分享的具体步骤。 首先,我们需要在新浪开放平台(http://open.weibo.com/)注册开发者账号,创建一个新的应用,获取到App Key、App ...
总结,实现"android listview 下拉刷新 上拉翻页 仿新浪微博客户端"的功能,关键在于选用合适的第三方库(如XListView)并正确设置和监听其刷新和加载事件。同时,合理的数据加载策略、动画效果以及状态处理都是提升...