以前喜歡看小說的時候,在手機上下載了一些讀書軟體,軟體中有個添加書籤的功能很炫;微博中重新整理資訊列表的方式也很酷。
現在以listview下拉重新整理列表為例,說明如何添加書籤,如何通過下拉、上拉方式重新整理listview。
先貼上:
在ListView中有addHeaderView()和addFooterView()兩個方法,可以在listview的頂部和底部放置兩個不同或者相同的自訂view,作為ListView的item元素;要添加這兩個view需要在ListView.setAdapter()方法之前使用才可。頂部和底部布局搞定之後,就需要怎麼實現下拉重新整理功能了,每個View對象都具有setPadding()方法來控制view對象本身與四周其他控制項或者螢幕邊框的距離,在這裡需要用到的是與螢幕頂部的距離變化,來控制頂部view的顯示效果;在頂部view中定義了一個"箭頭"控制項,用來標識方向,使用了RotateAnimation動畫控制"箭頭"的旋轉、指向。書籤的添加,也是類似的,可以在定義好的布局檔案頂部中增加一個view,在需要添加書籤的時候,通過setPadding()來控制該view。
首先需要建立一個主ListView類布局檔案activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_height="match_parent" android:layout_width="match_parent" tools:context=".MainActivity" > <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent" android:cacheColorHint="#00000000" android:divider="@android:color/transparent"/></RelativeLayout>
布局檔案中只顯示了一個ListView控制項,設定ListView滑動時背景為透明,Item之前線條為透明;之後定義頂部View檔案head.xml
<?xml version="1.0" encoding="utf-8"?><!-- ListView的頭部 --><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content"> <!-- 內容 --> <RelativeLayout android:id="@+id/head_contentLayout" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingLeft="30sp" > <!-- 箭頭映像、進度條 --> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_centerVertical="true" > <!-- 箭頭 --> <ImageView android:id="@+id/head_arrowImageView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/ic_pulltorefresh_arrow" /> <!-- 進度條 --> <ProgressBar android:id="@+id/head_progressBar" style="?android:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="gone" /> </FrameLayout> <!-- 提示、最新動向 --> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:gravity="center_horizontal" android:orientation="vertical" > <!-- 提示 --> <TextView android:id="@+id/head_tipsTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下拉重新整理" android:textColor="#000000" android:textSize="20sp" /> <!-- 最新動向 --> <TextView android:id="@+id/head_lastUpdatedTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="上次更新" android:textColor="#ff0000" android:textSize="10sp" /> </LinearLayout> </RelativeLayout></LinearLayout>
該布局中定義了"箭頭",重新整理提示,更新時間,進度條等內容,可按實際需求修改;之後定義底部View檔案bottom.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:gravity="center_horizontal" android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical" android:paddingBottom="13sp" android:paddingTop="13sp" > <TextView android:gravity="center_horizontal" android:id="@+id/more_id" android:layout_height="wrap_content" android:layout_width="fill_parent" android:text="查看更多..." android:textSize="25sp" /> <LinearLayout android:id="@+id/load_id" android:layout_height="wrap_content" android:layout_width="wrap_content" android:orientation="horizontal" android:visibility="gone" > <ProgressBar android:layout_gravity="center_horizontal" android:layout_height="wrap_content" android:layout_width="match_parent" /> <TextView android:gravity="center_vertical" android:layout_height="fill_parent" android:layout_marginLeft="10sp" android:layout_width="wrap_content" android:text="正在載入..." android:textSize="20sp" /> </LinearLayout></LinearLayout>
現在通過MainActivity.java將頂部檔案、主檔案、底部檔案綁定在一起實現下來重新整理、彈出效果等諸多功能
package com.example.pulldownrefresh;import java.util.ArrayList;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.LayoutInflater;import android.view.Menu;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.View.MeasureSpec;import android.view.View.OnTouchListener;import android.view.animation.LinearInterpolator;import android.view.animation.RotateAnimation;import android.view.animation.TranslateAnimation;import android.widget.Button;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.SimpleAdapter;import android.widget.TextView;/** * * @author tr * @time 2014-2-28 * @description 下拉重新整理列表 */public class MainActivity extends Activity implements OnTouchListener{/**listview執行個體對象*/private ListView mListView;private int headViewWidth, headViewHeight;// 實際的padding的距離與介面上位移距離的比例private final static int RATIO = 3;/**旋轉動畫*/private RotateAnimation animation,reverseAnimation;/**旋轉對象*/private ImageView head_arrowImageView;private float startY;private View headView;private SimpleAdapter adapter; private List<Map<String,Object>> data; private static final String TAG = "listview";private final static int RELEASE_To_REFRESH = 0;private final static int PULL_To_REFRESH = 1;private final static int REFRESHING = 2;private final static int DONE = 3;private final static int LOADING = 4;// 用於保證startY的值在一個完整的touch事件中只被記錄一次private boolean isRecored;private int firstItemIndex;private int state;private boolean isBack;private TextView tipsTextview;private TextView lastUpdatedTextView;private ProgressBar progressBar; //查看更多 private TextView moreTextView; //正在載入進度條 private LinearLayout loadProgressBar; //分頁載入的資料的數量 private int pageSize=10; private final int pageType=1;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = (ListView) findViewById(R.id.listview);mListView.setOnTouchListener(this);LayoutInflater layoutInflater = getLayoutInflater();animation = new RotateAnimation(0, 180,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);animation.setFillAfter(true);animation.setDuration(200);animation.setInterpolator(new LinearInterpolator());reverseAnimation = new RotateAnimation(180, 0,RotateAnimation.RELATIVE_TO_SELF, 0.5f,RotateAnimation.RELATIVE_TO_SELF, 0.5f);reverseAnimation.setFillAfter(true);reverseAnimation.setDuration(200);reverseAnimation.setInterpolator(new LinearInterpolator());headView = layoutInflater.from(this).inflate(R.layout.head, null);progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar);tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView);data = initValue(1,15);adapter = new SimpleAdapter(this, data, android.R.layout.simple_expandable_list_item_2, new String[] {"title","text"}, new int[] {android.R.id.text1,android.R.id.text2});head_arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);head_arrowImageView.setMinimumHeight(50);head_arrowImageView.setMinimumWidth(70);measureView(headView);addPageMore();headViewWidth = headView.getMeasuredWidth();headViewHeight = headView.getMeasuredHeight();headView.setPadding(0, -1*headViewHeight, 0, 0);headView.invalidate();mListView.addHeaderView(headView, null, false);mListView.setAdapter(adapter);} /** * 在ListView中添加"載入更多" */ private void addPageMore(){ View view=LayoutInflater.from(this).inflate(R.layout.bottom, null); moreTextView=(TextView)view.findViewById(R.id.more_id); loadProgressBar=(LinearLayout)view.findViewById(R.id.load_id); moreTextView.setOnClickListener(new Button.OnClickListener() { @Override public void onClick(View v) { //隱藏"載入更多" moreTextView.setVisibility(View.GONE); //顯示進度條 loadProgressBar.setVisibility(View.VISIBLE); new refreshTask().execute(); } }); mListView.addFooterView(view); } /** * 載入下一頁的資料 * @param pageStart * @param pageSize */ private void chageListView(int pageStart,int pageSize){ List<Map<String,Object>> data=initValue(pageStart,pageSize); for (Map<String, Object> map : data) { this.data.add(map); } data=null; }// 此方法直接照搬自網路上的一個下拉重新整理的demo,此處是“估計”headView的width以及heightprivate void measureView(View child) {ViewGroup.LayoutParams p = child.getLayoutParams();if (p == null) {p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);}int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);int lpHeight = p.height;int childHeightSpec;if (lpHeight > 0) {childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,MeasureSpec.EXACTLY);} else {childHeightSpec = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED);}child.measure(childWidthSpec, childHeightSpec);} /** * 類比資料分頁載入, * @param pageStart 起始數 * @param pageSize 每頁顯示數目 * @return */ public static List<Map<String,Object>> initValue(int pageStart,int pageSize){ Map<String,Object> map; List<Map<String,Object>> list=new ArrayList<Map<String,Object>>(); for(int i=0;i<pageSize;i++){ map=new HashMap<String,Object>(); map.put("text", "定義listview列表"); map.put("title", "第"+pageStart+"項"); ++pageStart; list.add(map); } return list; }@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (firstItemIndex == 0 && !isRecored) {isRecored = true;startY = (int) event.getY();Log.v(TAG, "在down時候記錄當前位置‘"+startY);}break;case MotionEvent.ACTION_UP:if (state != REFRESHING && state != LOADING) {if (state == DONE) {// 什麼都不做}if (state == PULL_To_REFRESH) {state = DONE;changeHeaderViewByState();Log.v(TAG, "由下拉重新整理狀態,到done狀態");}if (state == RELEASE_To_REFRESH) {state = REFRESHING;changeHeaderViewByState();Log.v(TAG, "由鬆開重新整理狀態,到done狀態");}}isRecored = false;isBack = false;break;case MotionEvent.ACTION_MOVE:int tempY = (int) event.getY();if (!isRecored && firstItemIndex == 0) {Log.v(TAG, "在move時候記錄下位置"+tempY);isRecored = true;startY = tempY;}if (state != REFRESHING && isRecored && state != LOADING) {// 保證在設定padding的過程中,當前的位置一直是在head,否則如果當列表超出螢幕的話,當在上推的時候,列表會同時進行滾動// 可以鬆手去重新整理了if (state == RELEASE_To_REFRESH) {mListView.setSelection(0);// 往上推了,推到了螢幕足夠掩蓋head的程度,但是還沒有推到全部掩蓋的地步if (((tempY - startY) / RATIO < headViewHeight)&& (tempY - startY) > 0) {state = PULL_To_REFRESH;changeHeaderViewByState();Log.v(TAG, "由鬆開重新整理狀態轉變到下拉重新整理狀態");}// 一下子推到頂了else if (tempY - startY <= 0) {state = DONE;changeHeaderViewByState();Log.v(TAG, "由鬆開重新整理狀態轉變到done狀態");}// 往下拉了,或者還沒有上推到螢幕頂部掩蓋head的地步else {// 不用進行特別的操作,只用更新paddingTop的值就行了}}// 還沒有到達顯示鬆開重新整理的時候,DONE或者是PULL_To_REFRESH狀態if (state == PULL_To_REFRESH) {// 下拉到可以進入RELEASE_TO_REFRESH的狀態if ((tempY - startY) / RATIO >= headViewHeight) {state = RELEASE_To_REFRESH;isBack = true;changeHeaderViewByState();Log.v(TAG, "由done或者下拉重新整理狀態轉變到鬆開重新整理");}// 上推到頂了else if (tempY - startY <= 0) {state = DONE;changeHeaderViewByState();Log.v(TAG, "由DOne或者下拉重新整理狀態轉變到done狀態");}}// done狀態下if (state == DONE) {if (tempY - startY > 0) {state = PULL_To_REFRESH;changeHeaderViewByState();}}// 更新headView的sizeif (state == PULL_To_REFRESH) {headView.setPadding(0, (int)(-1 * headViewHeight+ (tempY - startY) / RATIO), 0, 0);System.out.println("top_distance0:"+(-1 * headViewHeight + (tempY - startY) / RATIO));}// 更新headView的paddingTopif (state == RELEASE_To_REFRESH) {headView.setPadding(0, (int)((tempY - startY) / RATIO- headViewHeight), 0, 0);}}break;}return false;}// 當狀態改變時候,調用該方法,以更新介面private void changeHeaderViewByState() {switch (state) {case RELEASE_To_REFRESH:head_arrowImageView.setVisibility(View.VISIBLE);progressBar.setVisibility(View.GONE);tipsTextview.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.VISIBLE);head_arrowImageView.clearAnimation();head_arrowImageView.startAnimation(animation);tipsTextview.setText("鬆開重新整理");Log.v(TAG, "目前狀態,鬆開重新整理");break;case PULL_To_REFRESH:progressBar.setVisibility(View.GONE);tipsTextview.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.VISIBLE);head_arrowImageView.clearAnimation();head_arrowImageView.setVisibility(View.VISIBLE);// 是由RELEASE_To_REFRESH狀態轉變來的if (isBack) {isBack = false;head_arrowImageView.clearAnimation();head_arrowImageView.startAnimation(reverseAnimation);tipsTextview.setText("下拉重新整理");} else {tipsTextview.setText("下拉重新整理");}Log.v(TAG, "目前狀態,下拉重新整理");break;case REFRESHING:headView.setPadding(0, 0, 0, 0);progressBar.setVisibility(View.VISIBLE);head_arrowImageView.clearAnimation();head_arrowImageView.setVisibility(View.GONE);tipsTextview.setText("正在重新整理...");lastUpdatedTextView.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.GONE);Log.v(TAG, "目前狀態,正在重新整理...");new refreshTask().execute();break;case DONE:headView.setPadding(0, -1 * headViewHeight, 0, 0);progressBar.setVisibility(View.GONE);head_arrowImageView.clearAnimation();head_arrowImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);tipsTextview.setText("下拉重新整理");lastUpdatedTextView.setVisibility(View.VISIBLE);Log.v(TAG, "目前狀態,done");break;}}class refreshTask extends AsyncTask<Void, Void, Void>{@Overrideprotected Void doInBackground(Void... params) {// TODO Auto-generated method stub//需要在載入過程中做的事情try{Thread.sleep(3000);}catch(Exception e){e.printStackTrace();} //載入類比資料:下一頁資料, 在正常情況下,上面的休眠是不需要,直接使用下面這句代碼載入相關資料 chageListView(data.size()+1,pageSize);return null;}@Overrideprotected void onProgressUpdate(Void... values) {// TODO Auto-generated method stubsuper.onProgressUpdate(values);//進度更新}@Overrideprotected void onPostExecute(Void result) {// TODO Auto-generated method stubsuper.onPostExecute(result);//載入完成之後,應該做什麼樣的操作 //通知適配器,發現改變操作 adapter.notifyDataSetChanged();onRefreshComplete(); //再次顯示"載入更多" moreTextView.setVisibility(View.VISIBLE); //再次隱藏“進度條” loadProgressBar.setVisibility(View.GONE);}}/**重新整理完成,還原介面各種狀態,修改更新時間*/public void onRefreshComplete() {state = DONE;//更新最新重新整理時間lastUpdatedTextView.setText("最新動向:" + new Date().toLocaleString());changeHeaderViewByState();}}書籤功能,可按照類似原理開發,之後有時間將提供源碼。
原始碼,點擊下載