android上拉下拉加载更多数据

最近项目中用到了ListView的下拉刷新的功能,总结了一下前辈们的代码,单独抽取出来写了一个demo作为示例。

效果图

下拉刷新:

加载更多:

CustomListView.java

package com.example.uitest.view;  
 
import java.util.Date;  
import com.example.uitest.R;  
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.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

<?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="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" 
 style="?android:attr/progressBarStyleSmall" 
 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> 

listfooter_more.xml

<?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" 
 style="@android:style/Widget.ProgressBar.Small.Inverse" 
 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> 

MainActivity.java

package com.example.uitest;  
 
import java.util.ArrayList;  
import java.util.List;  
 
import android.app.Activity;  
import android.content.Context;  
import android.graphics.BitmapFactory;  
import android.os.Bundle;  
import android.os.Handler;  
import android.util.Log;  
import android.view.LayoutInflater;  
import android.view.Menu;  
import android.view.View;  
import android.view.ViewGroup;  
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;  
 
    }  
}  

list_item.xml

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
 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> 

工程下载地址:http://download.csdn.net/detail/fx_sky/5646017

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏非著名程序员

Android实现滑动刻度尺效果,选择身高体重和生日

刻度尺效果虽然看起来很美,我个人认为很不实用,即使再不实用,也有用的,鉴于群里成员对我的苦苦哀求,我就分享一个他用不到的,横屏滑动刻度尺,因为他需要竖屏的,哈哈...

275100
来自专栏编程之路

羊皮书APP(Android版)开发系列(十七)Android 底部菜单栏实现

18820
来自专栏Android干货

安卓开发_慕课网_Fragment实现Tab(App主界面)

374100
来自专栏Android开发指南

6.侧边栏菜单详情页

370130
来自专栏非著名程序员

Android实现竖着的滑动刻度尺效果,选择身高(竖向的)

这次是你想要的效果哦!高兴了吧?高兴地话,你不用给我打赏,分享到朋友圈,我就很高兴了。 群里有人问我要竖着的滑动尺效果,前天我贱贱地分享了一个横向的滑动效果,...

24460
来自专栏编程之路

羊皮书APP(Android版)开发系列(十五)Android 循环滚动,自动轮播广告展示

31250
来自专栏向治洪

toggbutton

2013年8月14日Android记录 很多应用都会有用户设置,用户的一些偏好可以由用户来决定那是应用人性化的体现,在实际开发中很多情况都作成可配置的了,本篇博...

25490
来自专栏Android干货

安卓开发_慕课网_ViewPager实现Tab(App主界面)

29870
来自专栏Android开发指南

16.语音识别

50790
来自专栏Android干货园

Android 高仿微信发朋友圈浏览图片效果

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/50...

82810

扫码关注云+社区

领取腾讯云代金券