android上拉下拉加载更多数据
2023-09-14 08:58:19 时间
最近项目中用到了ListView的下拉刷新的功能,总结了一下前辈们的代码,单独抽取出来写了一个demo作为示例。
p br /p
p 效果图 /p
p 下拉刷新: /p
p img src= http://img.blog.csdn.net/20130625115351921?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZX
import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; * ListView下拉刷新 */ public class CustomListView extends ListView implements OnScrollListener { 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; // 实际的padding的距离与界面上偏移距离的比例 private final static int RATIO = 3; private LayoutInflater inflater; private LinearLayout headView; private TextView tipsTextview; private TextView lastUpdatedTextView; private ImageView arrowImageView; private ProgressBar progressBar; private RotateAnimation animation; private RotateAnimation reverseAnimation; // 用于保证startY的值在一个完整的touch事件中只被记录一次 private boolean isRecored; private int headContentWidth; private int headContentHeight; private int startY; private int firstItemIndex; private int state; private boolean isBack; private OnRefreshListener refreshListener; private OnLoadListener loadListener; private boolean isRefreshable; private ProgressBar moreProgressBar; private TextView loadMoreView; private View moreView; public CustomListView(Context context) { super(context); init(context); } public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { setCacheColorHint(context.getResources().getColor(R.color.transparent)); inflater = LayoutInflater.from(context); headView = (LinearLayout) inflater.inflate(R.layout.head, null); arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView); arrowImageView.setMinimumWidth(70); arrowImageView.setMinimumHeight(50); progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar); tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView); lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView); measureView(headView); headContentHeight = headView.getMeasuredHeight(); headContentWidth = headView.getMeasuredWidth(); headView.setPadding(0, -1 * headContentHeight, 0, 0); headView.invalidate(); Log.v("size", "width:" + headContentWidth + " height:" + headContentHeight); addHeaderView(headView, null, false); setOnScrollListener(this); 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); state = DONE; isRefreshable = false; moreView = LayoutInflater.from(context).inflate(R.layout.listfooter_more, null); moreView.setVisibility(View.VISIBLE); moreProgressBar = (ProgressBar) moreView.findViewById(R.id.pull_to_refresh_progress); loadMoreView = (TextView) moreView.findViewById(R.id.load_more); moreView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onLoad(); } }); addFooterView(moreView); } public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2, int arg3) { firstItemIndex = firstVisiableItem; } public void onScrollStateChanged(AbsListView arg0, int arg1) { } public boolean onTouchEvent(MotionEvent event) { if (isRefreshable) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (firstItemIndex == 0 !isRecored) { isRecored = true; startY = (int) event.getY(); } break; case MotionEvent.ACTION_UP: if (state != REFRESHING state != LOADING) { if (state == DONE) { } if (state == PULL_To_REFRESH) { state = DONE; changeHeaderViewByState(); } if (state == RELEASE_To_REFRESH) { state = REFRESHING; changeHeaderViewByState(); onRefresh(); } } isRecored = false; isBack = false; break; case MotionEvent.ACTION_MOVE: int tempY = (int) event.getY(); if (!isRecored firstItemIndex == 0) { isRecored = true; startY = tempY; } if (state != REFRESHING isRecored state != LOADING) { // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动 // 可以松手去刷新了 if (state == RELEASE_To_REFRESH) { setSelection(0); // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步 if (((tempY - startY) / RATIO headContentHeight) (tempY - startY) 0) { state = PULL_To_REFRESH; changeHeaderViewByState(); } // 一下子推到顶了 else if (tempY - startY = 0) { state = DONE; changeHeaderViewByState(); } // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步 } // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态 if (state == PULL_To_REFRESH) { setSelection(0); // 下拉到可以进入RELEASE_TO_REFRESH的状态 if ((tempY - startY) / RATIO = headContentHeight) { state = RELEASE_To_REFRESH; isBack = true; changeHeaderViewByState(); } else if (tempY - startY = 0) { state = DONE; changeHeaderViewByState(); } } if (state == DONE) { if (tempY - startY 0) { state = PULL_To_REFRESH; changeHeaderViewByState(); } } if (state == PULL_To_REFRESH) { headView.setPadding(0, -1 * headContentHeight + (tempY - startY) / RATIO, 0, 0); } if (state == RELEASE_To_REFRESH) { headView.setPadding(0, (tempY - startY) / RATIO - headContentHeight, 0, 0); } } break; } } return super.onTouchEvent(event); } // 当状态改变时候,调用该方法,以更新界面 private void changeHeaderViewByState() { switch (state) { case RELEASE_To_REFRESH: arrowImageView.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); tipsTextview.setVisibility(View.VISIBLE); lastUpdatedTextView.setVisibility(View.VISIBLE); arrowImageView.clearAnimation(); arrowImageView.startAnimation(animation); tipsTextview.setText("松开刷新"); break; case PULL_To_REFRESH: progressBar.setVisibility(View.GONE); tipsTextview.setVisibility(View.VISIBLE); lastUpdatedTextView.setVisibility(View.VISIBLE); arrowImageView.clearAnimation(); arrowImageView.setVisibility(View.VISIBLE); // 是由RELEASE_To_REFRESH状态转变来的 if (isBack) { isBack = false; arrowImageView.clearAnimation(); arrowImageView.startAnimation(reverseAnimation); tipsTextview.setText("下拉刷新"); } else { tipsTextview.setText("下拉刷新"); } break; case REFRESHING: headView.setPadding(0, 0, 0, 0); progressBar.setVisibility(View.VISIBLE); arrowImageView.clearAnimation(); arrowImageView.setVisibility(View.GONE); tipsTextview.setText("正在刷新..."); lastUpdatedTextView.setVisibility(View.VISIBLE); break; case DONE: headView.setPadding(0, -1 * headContentHeight, 0, 0); progressBar.setVisibility(View.GONE); arrowImageView.clearAnimation(); arrowImageView.setImageResource(R.drawable.arrow); tipsTextview.setText("下拉刷新"); lastUpdatedTextView.setVisibility(View.VISIBLE); break; } } public void setonRefreshListener(OnRefreshListener refreshListener) { this.refreshListener = refreshListener; isRefreshable = true; } public void setonLoadListener(OnLoadListener loadListener) { this.loadListener = loadListener; } public interface OnRefreshListener { public void onRefresh(); } public interface OnLoadListener { public void onLoad(); } @SuppressWarnings("deprecation") public void onRefreshComplete() { state = DONE; lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString()); changeHeaderViewByState(); } private void onLoad() { if (loadListener != null) { moreProgressBar.setVisibility(View.VISIBLE); loadMoreView.setText(getContext().getString(R.string.load_more)); loadListener.onLoad(); } } public void onLoadComplete() { // moreView.setVisibility(View.GONE); moreProgressBar.setVisibility(View.GONE); loadMoreView.setText(getContext().getString(R.string.more_data)); } private void onRefresh() { if (refreshListener != null) { refreshListener.onRefresh(); } } private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_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); } @SuppressWarnings("deprecation") public void setAdapter(BaseAdapter adapter) { lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString()); super.setAdapter(adapter); }
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="30dp" !-- 箭头图像、进度条 -- 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:contentDescription="@string/app_name" android:src="@drawable/arrow" / !-- 进度条 -- ProgressBar android:id="@+id/head_progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:indeterminateDrawable="@drawable/progressbar_bg" 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="@string/pull_to_refresh_pull_label" android:textColor="@color/pull_refresh_textview" android:textSize="20sp" / !-- 最近更新 -- TextView android:id="@+id/head_lastUpdatedTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pull_to_refresh_refresh_lasttime" android:textColor="@color/gold" android:textSize="10sp" / /LinearLayout /RelativeLayout /LinearLayout
?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="fill_parent" android:gravity="center_horizontal" android:orientation="horizontal" android:padding="15dp" ProgressBar android:id="@+id/pull_to_refresh_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:indeterminate="true" android:visibility="gone" /ProgressBar TextView android:id="@+id/load_more" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10.0dp" android:gravity="center" android:text="@string/more_data" android:textColor="@color/black" /TextView /LinearLayout
import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.example.uitest.model.AppInfo; import com.example.uitest.view.CustomListView; import com.example.uitest.view.CustomListView.OnLoadListener; import com.example.uitest.view.CustomListView.OnRefreshListener; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); private static final int LOAD_DATA_FINISH = 10; private static final int REFRESH_DATA_FINISH = 11; private List AppInfo mList = new ArrayList AppInfo private CustomListAdapter mAdapter; private CustomListView mListView; private int count = 10; private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case REFRESH_DATA_FINISH: if(mAdapter!=null){ mAdapter.notifyDataSetChanged(); } mListView.onRefreshComplete(); //下拉刷新完成 break; case LOAD_DATA_FINISH: if(mAdapter!=null){ mAdapter.notifyDataSetChanged(); } mListView.onLoadComplete(); //加载更多完成 break; default: break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); buildAppData(); mAdapter = new CustomListAdapter(this); mListView = (CustomListView) findViewById(R.id.mListView); mListView.setAdapter(mAdapter); mListView.setonRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { //TODO 下拉刷新 Log.e(TAG, "onRefresh"); loadData(0); } }); mListView.setonLoadListener(new OnLoadListener() { @Override public void onLoad() { //TODO 加载更多 Log.e(TAG, "onLoad"); loadData(1); } }); mListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView ? parent, View view, int position, long id) { Log.e(TAG, "click position:" + position); } }); } public void loadData(final int type){ new Thread(){ @Override public void run() { for(int i=count;i count+10;i++){ AppInfo ai = new AppInfo(); ai.setAppIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)); ai.setAppName("应用Demo_" + i); ai.setAppVer("版本: " + (i % 10 + 1) + "." + (i % 8 + 2) + "." + (i % 6 + 3)); ai.setAppSize("大小: " + i * 10 + "MB"); mList.add(ai); } count += 10; try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } if(type==0){ //下拉刷新 // Collections.reverse(mList); //逆序 handler.sendEmptyMessage(REFRESH_DATA_FINISH); }else if(type==1){ handler.sendEmptyMessage(LOAD_DATA_FINISH); } } }.start(); } /** * 初始化应用数据 */ private void buildAppData() { for (int i = 0; i 10; i++) { AppInfo ai = new AppInfo(); ai.setAppIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)); ai.setAppName("应用Demo_" + i); ai.setAppVer("版本: " + (i % 10 + 1) + "." + (i % 8 + 2) + "." + (i % 6 + 3)); ai.setAppSize("大小: " + i * 10 + "MB"); mList.add(ai); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } public class CustomListAdapter extends BaseAdapter { private LayoutInflater mInflater; public CustomListAdapter(Context context) { mInflater = LayoutInflater.from(context); } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int arg0) { return mList.get(arg0); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (getCount() == 0) { return null; } ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); holder.ivImage = (ImageView) convertView .findViewById(R.id.ivIcon); holder.tvName = (TextView) convertView .findViewById(R.id.tvName); holder.tvVer = (TextView) convertView.findViewById(R.id.tvVer); holder.tvSize = (TextView) convertView .findViewById(R.id.tvSize); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } AppInfo ai = mList.get(position); holder.ivImage.setImageBitmap(ai.getAppIcon()); holder.tvName.setText(ai.getAppName()); holder.tvVer.setText(ai.getAppVer()); holder.tvSize.setText(ai.getAppSize()); return convertView; } } public static class ViewHolder { private ImageView ivImage; private TextView tvName; private TextView tvVer; private TextView tvSize; }
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" ImageView android:id="@+id/ivIcon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/image_desc" android:src="@drawable/ic_launcher" / LinearLayout android:id="@+id/appInfo" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_toRightOf="@id/ivIcon" android:orientation="vertical" TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/name" android:textColor="#000000" android:textSize="16sp" / TextView android:id="@+id/tvVer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ver" android:textColor="#666666" android:textSize="13sp" / TextView android:id="@+id/tvSize" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/size" android:textColor="#666666" android:textSize="13sp" / /LinearLayout Button android:id="@+id/btnClick" android:layout_width="80dip" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:focusable="false" android:text="@string/mgr" android:textColor="#000000" android:textSize="16sp" / /RelativeLayout
一文读懂系列Android屏幕刷新机制 对一些大中型项目来说可能就不一样了:**他们涉及业务较多,设备种类较多,往往一个app内部集成了十几个子业务甚至上百个,这对应用性能要求就更加严格了,app的体验也会间接导致用户的留存问题**。 所以学习屏幕绘制这类理论性较强的知识也是非常有必要的。
在不丢失数据的情况下处理屏幕旋转 - Android 我正在疯狂地弄清楚处理屏幕旋转的最佳方法是什么。 如何在重新创建活动之前保存 myClass 数据,以便我可以保留重绘活动的所有内容,而无需再次进行无用的初始化? 有没有比 Parcelable 更清洁、更好的方法? 我需要处理旋转,因为我想在横向模式下更改布局。
小菜在适配 Android8.0 过程中,遇到很多问题,有很多很常见的问题,今天来整理一下页面跳转时黑屏的问题。 显示 Activity 页面之前会优先开启一个 Starting Window(Preview Window),等待 Activity 加载完成之后显示 UI 界面,猜测在这个等待过程中可能会出现页面跳转时的短暂黑屏。
小菜前段时间根据超多 star 的 FlycoTabLayout 自己修改封装了仿网易顶部滑动标题栏 TabSlideLayout 滑动内容可以是文字也可以是网络图标,并整理了两篇小博客: Android 优化个人封装仿网易新闻可滑动标题...
自定义view实现 思路分析:通过canvas画圆,每次改变圆半径和透明度,当半径达到一定程度,再次从中心开始绘圆,达到不同层级...
看过我这边文章的RecyclerView.Adapter的优化与封装的真的想说声抱歉,其实不需要继承BaseBean,只需要继承Object就可以了,而且更灵活了,当时不知道咋了,脑袋抽风了,多次了这一举。
import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; * ListView下拉刷新 */ public class CustomListView extends ListView implements OnScrollListener { 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; // 实际的padding的距离与界面上偏移距离的比例 private final static int RATIO = 3; private LayoutInflater inflater; private LinearLayout headView; private TextView tipsTextview; private TextView lastUpdatedTextView; private ImageView arrowImageView; private ProgressBar progressBar; private RotateAnimation animation; private RotateAnimation reverseAnimation; // 用于保证startY的值在一个完整的touch事件中只被记录一次 private boolean isRecored; private int headContentWidth; private int headContentHeight; private int startY; private int firstItemIndex; private int state; private boolean isBack; private OnRefreshListener refreshListener; private OnLoadListener loadListener; private boolean isRefreshable; private ProgressBar moreProgressBar; private TextView loadMoreView; private View moreView; public CustomListView(Context context) { super(context); init(context); } public CustomListView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context) { setCacheColorHint(context.getResources().getColor(R.color.transparent)); inflater = LayoutInflater.from(context); headView = (LinearLayout) inflater.inflate(R.layout.head, null); arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView); arrowImageView.setMinimumWidth(70); arrowImageView.setMinimumHeight(50); progressBar = (ProgressBar) headView.findViewById(R.id.head_progressBar); tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView); lastUpdatedTextView = (TextView) headView.findViewById(R.id.head_lastUpdatedTextView); measureView(headView); headContentHeight = headView.getMeasuredHeight(); headContentWidth = headView.getMeasuredWidth(); headView.setPadding(0, -1 * headContentHeight, 0, 0); headView.invalidate(); Log.v("size", "width:" + headContentWidth + " height:" + headContentHeight); addHeaderView(headView, null, false); setOnScrollListener(this); 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); state = DONE; isRefreshable = false; moreView = LayoutInflater.from(context).inflate(R.layout.listfooter_more, null); moreView.setVisibility(View.VISIBLE); moreProgressBar = (ProgressBar) moreView.findViewById(R.id.pull_to_refresh_progress); loadMoreView = (TextView) moreView.findViewById(R.id.load_more); moreView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onLoad(); } }); addFooterView(moreView); } public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2, int arg3) { firstItemIndex = firstVisiableItem; } public void onScrollStateChanged(AbsListView arg0, int arg1) { } public boolean onTouchEvent(MotionEvent event) { if (isRefreshable) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (firstItemIndex == 0 !isRecored) { isRecored = true; startY = (int) event.getY(); } break; case MotionEvent.ACTION_UP: if (state != REFRESHING state != LOADING) { if (state == DONE) { } if (state == PULL_To_REFRESH) { state = DONE; changeHeaderViewByState(); } if (state == RELEASE_To_REFRESH) { state = REFRESHING; changeHeaderViewByState(); onRefresh(); } } isRecored = false; isBack = false; break; case MotionEvent.ACTION_MOVE: int tempY = (int) event.getY(); if (!isRecored firstItemIndex == 0) { isRecored = true; startY = tempY; } if (state != REFRESHING isRecored state != LOADING) { // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动 // 可以松手去刷新了 if (state == RELEASE_To_REFRESH) { setSelection(0); // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步 if (((tempY - startY) / RATIO headContentHeight) (tempY - startY) 0) { state = PULL_To_REFRESH; changeHeaderViewByState(); } // 一下子推到顶了 else if (tempY - startY = 0) { state = DONE; changeHeaderViewByState(); } // 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步 } // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态 if (state == PULL_To_REFRESH) { setSelection(0); // 下拉到可以进入RELEASE_TO_REFRESH的状态 if ((tempY - startY) / RATIO = headContentHeight) { state = RELEASE_To_REFRESH; isBack = true; changeHeaderViewByState(); } else if (tempY - startY = 0) { state = DONE; changeHeaderViewByState(); } } if (state == DONE) { if (tempY - startY 0) { state = PULL_To_REFRESH; changeHeaderViewByState(); } } if (state == PULL_To_REFRESH) { headView.setPadding(0, -1 * headContentHeight + (tempY - startY) / RATIO, 0, 0); } if (state == RELEASE_To_REFRESH) { headView.setPadding(0, (tempY - startY) / RATIO - headContentHeight, 0, 0); } } break; } } return super.onTouchEvent(event); } // 当状态改变时候,调用该方法,以更新界面 private void changeHeaderViewByState() { switch (state) { case RELEASE_To_REFRESH: arrowImageView.setVisibility(View.VISIBLE); progressBar.setVisibility(View.GONE); tipsTextview.setVisibility(View.VISIBLE); lastUpdatedTextView.setVisibility(View.VISIBLE); arrowImageView.clearAnimation(); arrowImageView.startAnimation(animation); tipsTextview.setText("松开刷新"); break; case PULL_To_REFRESH: progressBar.setVisibility(View.GONE); tipsTextview.setVisibility(View.VISIBLE); lastUpdatedTextView.setVisibility(View.VISIBLE); arrowImageView.clearAnimation(); arrowImageView.setVisibility(View.VISIBLE); // 是由RELEASE_To_REFRESH状态转变来的 if (isBack) { isBack = false; arrowImageView.clearAnimation(); arrowImageView.startAnimation(reverseAnimation); tipsTextview.setText("下拉刷新"); } else { tipsTextview.setText("下拉刷新"); } break; case REFRESHING: headView.setPadding(0, 0, 0, 0); progressBar.setVisibility(View.VISIBLE); arrowImageView.clearAnimation(); arrowImageView.setVisibility(View.GONE); tipsTextview.setText("正在刷新..."); lastUpdatedTextView.setVisibility(View.VISIBLE); break; case DONE: headView.setPadding(0, -1 * headContentHeight, 0, 0); progressBar.setVisibility(View.GONE); arrowImageView.clearAnimation(); arrowImageView.setImageResource(R.drawable.arrow); tipsTextview.setText("下拉刷新"); lastUpdatedTextView.setVisibility(View.VISIBLE); break; } } public void setonRefreshListener(OnRefreshListener refreshListener) { this.refreshListener = refreshListener; isRefreshable = true; } public void setonLoadListener(OnLoadListener loadListener) { this.loadListener = loadListener; } public interface OnRefreshListener { public void onRefresh(); } public interface OnLoadListener { public void onLoad(); } @SuppressWarnings("deprecation") public void onRefreshComplete() { state = DONE; lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString()); changeHeaderViewByState(); } private void onLoad() { if (loadListener != null) { moreProgressBar.setVisibility(View.VISIBLE); loadMoreView.setText(getContext().getString(R.string.load_more)); loadListener.onLoad(); } } public void onLoadComplete() { // moreView.setVisibility(View.GONE); moreProgressBar.setVisibility(View.GONE); loadMoreView.setText(getContext().getString(R.string.more_data)); } private void onRefresh() { if (refreshListener != null) { refreshListener.onRefresh(); } } private void measureView(View child) { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_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); } @SuppressWarnings("deprecation") public void setAdapter(BaseAdapter adapter) { lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString()); super.setAdapter(adapter); }
在 CustomListView 中有2个回调接口,OnRefreshListener 和 OnLoadListener ,分别对应 下拉和点击加载更多 时候的回调函数。在下拉刷新完成之后要调用 mListView.onRefreshComplete(); 来隐藏掉 头部,调用 mListView.onLoadComplete(); 隐藏掉 底部的加载view。
header.xml
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="30dp" !-- 箭头图像、进度条 -- 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:contentDescription="@string/app_name" android:src="@drawable/arrow" / !-- 进度条 -- ProgressBar android:id="@+id/head_progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:indeterminateDrawable="@drawable/progressbar_bg" 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="@string/pull_to_refresh_pull_label" android:textColor="@color/pull_refresh_textview" android:textSize="20sp" / !-- 最近更新 -- TextView android:id="@+id/head_lastUpdatedTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pull_to_refresh_refresh_lasttime" android:textColor="@color/gold" android:textSize="10sp" / /LinearLayout /RelativeLayout /LinearLayout
?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="fill_parent" android:gravity="center_horizontal" android:orientation="horizontal" android:padding="15dp" ProgressBar android:id="@+id/pull_to_refresh_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:indeterminate="true" android:visibility="gone" /ProgressBar TextView android:id="@+id/load_more" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10.0dp" android:gravity="center" android:text="@string/more_data" android:textColor="@color/black" /TextView /LinearLayout
import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.example.uitest.model.AppInfo; import com.example.uitest.view.CustomListView; import com.example.uitest.view.CustomListView.OnLoadListener; import com.example.uitest.view.CustomListView.OnRefreshListener; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); private static final int LOAD_DATA_FINISH = 10; private static final int REFRESH_DATA_FINISH = 11; private List AppInfo mList = new ArrayList AppInfo private CustomListAdapter mAdapter; private CustomListView mListView; private int count = 10; private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case REFRESH_DATA_FINISH: if(mAdapter!=null){ mAdapter.notifyDataSetChanged(); } mListView.onRefreshComplete(); //下拉刷新完成 break; case LOAD_DATA_FINISH: if(mAdapter!=null){ mAdapter.notifyDataSetChanged(); } mListView.onLoadComplete(); //加载更多完成 break; default: break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); buildAppData(); mAdapter = new CustomListAdapter(this); mListView = (CustomListView) findViewById(R.id.mListView); mListView.setAdapter(mAdapter); mListView.setonRefreshListener(new OnRefreshListener() { @Override public void onRefresh() { //TODO 下拉刷新 Log.e(TAG, "onRefresh"); loadData(0); } }); mListView.setonLoadListener(new OnLoadListener() { @Override public void onLoad() { //TODO 加载更多 Log.e(TAG, "onLoad"); loadData(1); } }); mListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView ? parent, View view, int position, long id) { Log.e(TAG, "click position:" + position); } }); } public void loadData(final int type){ new Thread(){ @Override public void run() { for(int i=count;i count+10;i++){ AppInfo ai = new AppInfo(); ai.setAppIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)); ai.setAppName("应用Demo_" + i); ai.setAppVer("版本: " + (i % 10 + 1) + "." + (i % 8 + 2) + "." + (i % 6 + 3)); ai.setAppSize("大小: " + i * 10 + "MB"); mList.add(ai); } count += 10; try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } if(type==0){ //下拉刷新 // Collections.reverse(mList); //逆序 handler.sendEmptyMessage(REFRESH_DATA_FINISH); }else if(type==1){ handler.sendEmptyMessage(LOAD_DATA_FINISH); } } }.start(); } /** * 初始化应用数据 */ private void buildAppData() { for (int i = 0; i 10; i++) { AppInfo ai = new AppInfo(); ai.setAppIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher)); ai.setAppName("应用Demo_" + i); ai.setAppVer("版本: " + (i % 10 + 1) + "." + (i % 8 + 2) + "." + (i % 6 + 3)); ai.setAppSize("大小: " + i * 10 + "MB"); mList.add(ai); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } public class CustomListAdapter extends BaseAdapter { private LayoutInflater mInflater; public CustomListAdapter(Context context) { mInflater = LayoutInflater.from(context); } @Override public int getCount() { return mList.size(); } @Override public Object getItem(int arg0) { return mList.get(arg0); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (getCount() == 0) { return null; } ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.list_item, null); holder = new ViewHolder(); holder.ivImage = (ImageView) convertView .findViewById(R.id.ivIcon); holder.tvName = (TextView) convertView .findViewById(R.id.tvName); holder.tvVer = (TextView) convertView.findViewById(R.id.tvVer); holder.tvSize = (TextView) convertView .findViewById(R.id.tvSize); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } AppInfo ai = mList.get(position); holder.ivImage.setImageBitmap(ai.getAppIcon()); holder.tvName.setText(ai.getAppName()); holder.tvVer.setText(ai.getAppVer()); holder.tvSize.setText(ai.getAppSize()); return convertView; } } public static class ViewHolder { private ImageView ivImage; private TextView tvName; private TextView tvVer; private TextView tvSize; }
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" ImageView android:id="@+id/ivIcon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/image_desc" android:src="@drawable/ic_launcher" / LinearLayout android:id="@+id/appInfo" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="5dip" android:layout_toRightOf="@id/ivIcon" android:orientation="vertical" TextView android:id="@+id/tvName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/name" android:textColor="#000000" android:textSize="16sp" / TextView android:id="@+id/tvVer" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/ver" android:textColor="#666666" android:textSize="13sp" / TextView android:id="@+id/tvSize" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/size" android:textColor="#666666" android:textSize="13sp" / /LinearLayout Button android:id="@+id/btnClick" android:layout_width="80dip" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:focusable="false" android:text="@string/mgr" android:textColor="#000000" android:textSize="16sp" / /RelativeLayout
一文读懂系列Android屏幕刷新机制 对一些大中型项目来说可能就不一样了:**他们涉及业务较多,设备种类较多,往往一个app内部集成了十几个子业务甚至上百个,这对应用性能要求就更加严格了,app的体验也会间接导致用户的留存问题**。 所以学习屏幕绘制这类理论性较强的知识也是非常有必要的。
在不丢失数据的情况下处理屏幕旋转 - Android 我正在疯狂地弄清楚处理屏幕旋转的最佳方法是什么。 如何在重新创建活动之前保存 myClass 数据,以便我可以保留重绘活动的所有内容,而无需再次进行无用的初始化? 有没有比 Parcelable 更清洁、更好的方法? 我需要处理旋转,因为我想在横向模式下更改布局。
小菜在适配 Android8.0 过程中,遇到很多问题,有很多很常见的问题,今天来整理一下页面跳转时黑屏的问题。 显示 Activity 页面之前会优先开启一个 Starting Window(Preview Window),等待 Activity 加载完成之后显示 UI 界面,猜测在这个等待过程中可能会出现页面跳转时的短暂黑屏。
小菜前段时间根据超多 star 的 FlycoTabLayout 自己修改封装了仿网易顶部滑动标题栏 TabSlideLayout 滑动内容可以是文字也可以是网络图标,并整理了两篇小博客: Android 优化个人封装仿网易新闻可滑动标题...
自定义view实现 思路分析:通过canvas画圆,每次改变圆半径和透明度,当半径达到一定程度,再次从中心开始绘圆,达到不同层级...
看过我这边文章的RecyclerView.Adapter的优化与封装的真的想说声抱歉,其实不需要继承BaseBean,只需要继承Object就可以了,而且更灵活了,当时不知道咋了,脑袋抽风了,多次了这一举。
相关文章
- [Android Pro] 网络流量安全测试工具Nogotofail
- [Android Pro] android 混淆文件project.properties和proguard-project.txt
- Unity3D研究院之与Android相互传递消息
- Android数据存储(二)----PreferenceFragment详解
- 6、Android中的NFC技术
- Android开发学习---android下的数据持久化,保存数据到rom文件,android_data目录下文件访问的权限控制
- 《android开发艺术探索》读书笔记(十)--Android的消息机制
- android 20个小游戏
- 什么是Android逆向?如何学习安卓逆向?Android逆向自学笔记入门到实战
- android 证书验证流程分析_Android签名机制之---签名验证过程详解
- Android 用主线程的Handler 延迟执行任务
- Android 12.0 屏蔽FallbackHome机制去掉android正在启动直接进入默认Launcher功能实现
- Android app不被列入Recents多任务列表
- vlc源码分析一 vlc-android native调试配置
- Android--数据库数据显示至屏幕
- Android RootTrustManager 证书校验简单分析
- Android使用代码消除App数据并重新启动设备
- Android重力加速度传感器数据去噪
- android存储阵列数据SharedPreferences
- Android相关属性的介绍:android:exported = true
- 一文2000字详细介绍Android APP 常见概念与 adb 命令
- 如何在真机上调试Android应用程序(图文详解)(zz)
- 2021最新Android架构师必备宝典《Android架构开发手册》含抖音、美团等大厂架构演进之路
- 【Android入门】4、数据持久化:文件、SharedPreferences 和 Sqlite