編輯:關於Android編程
接著上次來講,這次來動手寫一下listview的下拉刷新功能和上拉加載更多功能。
當然google在android4.0以上的API裡面的提供了一個可以下拉加載更多的控件,這個小圓圈加載控件在豆瓣,知乎日報裡面都有運用到,而我在下一篇博客也會提到。
先來了解一下最基本的listview的的加載功能吧。
首先是下拉刷新功能,我先說一下基本的思路。listveiw的面提供了一個addheader()方法,我們可以重寫listview,然後用addheader方法加載我們自定義的加載布局。然後就是隱藏這個header,然後復寫監聽方法OnScrollListener()和OnTouch()方法,最後再提供一個接口方法來讓用戶實現加載數據。具體的我在代碼裡面都注釋好了。
再來說一下上拉加載,這個相比於下拉加載就簡單多了,我們可以addfooter()方法添加布局,然後監聽OnScrollListener就可以了,當最後一個可見的item等於總數量的item時,就可以加載數據了。具體在代碼裡面斗注釋好了。
效果圖:
先發布局文件:
footer.xml
listitem.xml
package com.example.listview_pulltorefresh; import java.sql.Date; import java.text.SimpleDateFormat; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; public class RefreshListview extends ListView { private View header;// 頂部布局文件 private View footer;// 底部布局 private int headerHeight;// 頂部布局文件的高度 private int firstVisibleItem;// 當前第一個可見item的位置 private boolean isRemark;// 標記當前listviews是否最頂端摁下 private int startY;// 開始的Y值 private int mscrollState;// 當前listview的滾動狀態 private int state;// 當前狀態 private static final int NONE = 0;// 正常狀態 private static final int PULL = 1;// 下拉狀態 private static final int RELEASE = 2;// 松開狀態 private static final int REFRESHING = 3;// 刷新狀態 private int mtotalItemCount;//全部item的數量 private int lastVisableItem;//最後一個可見的item private boolean isLoading=false;//是否正在加載 public RefreshListview(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub initView(context); } public RefreshListview(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub initView(context); } public RefreshListview(Context context) { super(context); // TODO Auto-generated constructor stub initView(context); } /** * 添加頂部布局文件 * * @param context */ private void initView(Context context) { LayoutInflater inflater = LayoutInflater.from(context); footer = inflater.inflate(R.layout.footer_loading, null, false); header = inflater.inflate(R.layout.header_layout, null, false); measureView(header); headerHeight = header.getMeasuredHeight(); Log.i("test", "headHeight:" + headerHeight); topPadding(-headerHeight); this.addHeaderView(header); //先設置底部隱藏 footer.setVisibility(View.GONE); this.addFooterView(footer); this.setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub mscrollState = scrollState; //最後一個可見的item是總數量,並且當前滾動狀態停止,就加載數據 if (mtotalItemCount==lastVisableItem&&scrollState==OnScrollListener.SCROLL_STATE_IDLE) { if (!isLoading) { //加載數據 isLoading=true; footer.setVisibility(VISIBLE); mListener2.onReflashMore(); } } } /** * firstvisebleitem第一個可見的位置 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // TODO Auto-generated method stub lastVisableItem=firstVisibleItem+visibleItemCount; mtotalItemCount=totalItemCount; } }); this.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (firstVisibleItem == 0) { isRemark = true; startY = (int) event.getY(); } case MotionEvent.ACTION_MOVE: onMove(event); break; case MotionEvent.ACTION_UP: if (state == RELEASE) { state = REFRESHING; reflashViewByState(); mListener.onrReflash(); // 加載數據 // 在外部調用reflashcomplete } else if (state == PULL) { state = NONE; isRemark = false; reflashViewByState(); } break; default: break; } return false; } }); } /** * 通過view獲取layoutparams,然後初始化lp, 要調用measure()方法來設置子view的寬高 * measure的方法參數有變化的是用MeasureSpec.makeMeasureSpec設置 * 沒有變化的用getChildMeasureSpec()方法設置 * * @param view */ public void measureView(View view) { ViewGroup.LayoutParams lParams = view.getLayoutParams(); if (lParams == null) { lParams = new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); // spec左右邊距,padding內邊距 int width = ViewGroup.getChildMeasureSpec(0, 0, lParams.width); int height; int tempHeight = lParams.height; if (tempHeight > 0) { // 填充用exactly height = MeasureSpec.makeMeasureSpec(tempHeight, MeasureSpec.EXACTLY); } else { // 意思就是<=0時,則告訴父布局子view高度填充0 height = MeasureSpec .makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } view.measure(width, height); } } /** * 設置header布局的上邊距 * * @param topPadding */ public void topPadding(int topPadding) { header.setPadding(header.getPaddingLeft(), topPadding, header.getPaddingRight(), header.getPaddingBottom()); header.invalidate(); } /** * 判斷移動過程中的操作 * * @param event */ private void onMove(MotionEvent event) { // TODO Auto-generated method stub if (!isRemark) { return; } int tempY = (int) event.getY();// 獲取當前y int space = tempY - startY;// 顯示的高度 int topPadding = space - headerHeight;// 因為是要用負值,所以減去高度 switch (state) { case NONE: if (space > 0) { state = PULL; reflashViewByState(); } break; case PULL: topPadding(topPadding); // 大於heigh+30且在滑動時,則是可以刷新 if (space > headerHeight + 30 && mscrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { state = RELEASE; reflashViewByState(); } break; case RELEASE: topPadding(topPadding); // 為釋放狀態時,則可以回到下拉狀態 if (space < headerHeight + 30) { state = PULL; reflashViewByState(); } else if (space <= 0) { state = NONE; isRemark = false; reflashViewByState(); } break; case REFRESHING: break; default: break; } } /** * 改變下拉過程中的header布局中的控件的內容 */ public void reflashViewByState() { TextView tip = (TextView) header.findViewById(R.id.tip); ImageView arrow = (ImageView) header.findViewById(R.id.arrow); ProgressBar progressBar = (ProgressBar) header .findViewById(R.id.progress); // RotateAnimation旋轉動畫,旋轉的角度,相對與自己,中心位置 RotateAnimation animation = new RotateAnimation(0, 180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(500);// 時間間隔 animation.setFillAfter(true);// 保存狀態 RotateAnimation animation2 = new RotateAnimation(180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); animation2.setDuration(500);// 時間間隔 animation2.setFillAfter(true);// 保存狀態 switch (state) { case NONE: arrow.clearAnimation(); topPadding(-headerHeight); break; case PULL: arrow.setVisibility(View.VISIBLE); progressBar.setVisibility(GONE); arrow.clearAnimation(); arrow.setAnimation(animation2); tip.setText("下拉可以刷新"); break; case RELEASE: arrow.setVisibility(View.VISIBLE); progressBar.setVisibility(GONE); arrow.clearAnimation(); arrow.setAnimation(animation); tip.setText("松開可以刷新"); break; case REFRESHING: topPadding(50); arrow.clearAnimation(); arrow.setVisibility(View.GONE); progressBar.setVisibility(VISIBLE); tip.setText("正在刷新"); break; default: break; } } /** * 獲取完數據 */ public void reflashComplete() { state = NONE; isRemark = false; reflashViewByState(); TextView lastTimeReflash = (TextView) header .findViewById(R.id.lastrefresh_time); SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss"); Date date = new Date(System.currentTimeMillis()); String timeString = format.format(date); lastTimeReflash.setText(timeString); } /** * 底部加載完畢 */ public void reflashFooterComplete() { isLoading=false; footer.setVisibility(GONE); } /** * 刷新數據接口 * * @author nickming * */ public interface OnReflashListener { public void onrReflash(); } public OnReflashListener mListener;// 刷新數據的接口 public void setOnReflashListener(OnReflashListener listener) { mListener = listener; } /** * 加載更多接口 * @author nickming * */ public interface OnReflashMoreListener{ public void onReflashMore(); } public OnReflashMoreListener mListener2; public void setOnReflashMoreListener(OnReflashMoreListener listener) { mListener2=listener; } }
MainActivity.class
package com.example.listview_pulltorefresh; import java.util.ArrayList; import java.util.List; import java.util.UUID; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import com.example.listview_pulltorefresh.RefreshListview.OnReflashListener; import com.example.listview_pulltorefresh.RefreshListview.OnReflashMoreListener; public class MainActivity extends Activity { private RefreshListview mlistview; MyAdapter adapter; Listmdata; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mlistview = (RefreshListview) findViewById(R.id.listview); mdata = new ArrayList (); for (int i = 0; i < 20; i++) { DataBean dataBean = new DataBean(); dataBean.setTextString("數據列:" + i); mdata.add(dataBean); } adapter = new MyAdapter(this, mdata); mlistview.setAdapter(adapter); mlistview.setOnReflashListener(new OnReflashListener() { @Override public void onrReflash() { // TODO Auto-generated method stub // 模擬網絡延時 Handler mHandler = new Handler(); mHandler.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub // 獲取最新數據 addHeaderData(); // 通知布局顯示 adapter.notifyDataSetChanged(); // listview刷新 mlistview.reflashComplete(); } }, 3000); } }); mlistview.setOnReflashMoreListener(new OnReflashMoreListener() { @Override public void onReflashMore() { // TODO Auto-generated method stub Handler mHandler=new Handler(); mHandler.postDelayed(new Runnable() { @Override public void run() { // TODO Auto-generated method stub addFooterData(); adapter.notifyDataSetChanged(); mlistview.reflashFooterComplete(); } }, 2000); } }); } public void addHeaderData() { for (int i = 0; i < 10; i++) { DataBean dataBean = new DataBean(); String nameString = UUID.randomUUID().toString(); dataBean.setTextString("最新數據:" + nameString); mdata.add(0, dataBean);// 放在最前面 } } public void addFooterData() { for (int i = 0; i < 10; i++) { DataBean dataBean = new DataBean(); String nameString = UUID.randomUUID().toString(); dataBean.setTextString("最新數據:" + nameString); mdata.add( dataBean);// 放在最後面 } } }
package com.example.listview_pulltorefresh; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; public class MyAdapter extends BaseAdapter { private Context mcontext; private ListmData; LayoutInflater inflater; public MyAdapter(Context context, List mData) { super(); this.mcontext = context; this.mData = mData; inflater=LayoutInflater.from(context); } @Override public int getCount() { // TODO Auto-generated method stub return mData.size(); } @Override public Object getItem(int position) { // TODO Auto-generated method stub return mData.get(position); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub viewHolder holder=null; if (convertView == null) { convertView = inflater.inflate(R.layout.listitem, parent,false); holder = new viewHolder(); holder.textView = (TextView) convertView .findViewById(R.id.textView1); convertView.setTag(holder); }else { holder=(viewHolder) convertView.getTag(); } holder.textView.setText(mData.get(position).getTextString()); return convertView; } class viewHolder { TextView textView; } }
package com.example.listview_pulltorefresh; import android.widget.TextView; public class DataBean { String textString; public DataBean() { // TODO Auto-generated constructor stub } public DataBean(String textString) { super(); this.textString = textString; } public String getTextString() { return textString; } public void setTextString(String textString) { this.textString = textString; } }
我們用webView去請求一個網頁鏈接的時候,如果請求網頁失敗或無網絡的情況下,它會返回給我們這樣一個頁面,如下圖所示: 上面這個頁面就是系統自帶的頁面,你覺得
Android Studio可以幫你在App中添加對URLs,app索引,搜索功能的支持。這些功能可以幫你推動更多的流量到你的App、發現App中最被常用的內容,使用戶更
Service常見面試題Service 是否在 main thread 中執行, service 裡面是否 能執行耗時的操作?默認情況,如果沒有顯示的指 servic 所
本文代碼以MTK平台Android 4.4為分析對象,與Google原生AOSP有些許差異,請讀者知悉。 本文主要介紹sim卡數據的讀取過程,當射頻狀態處於