android自定义刷新类控件

来源:互联网 发布:mac怎么玩梦幻西游手游 编辑:程序博客网 时间:2024/06/05 20:46

android虽然定义了种类非常丰富的控件,但是有的时候这些自定义的控件还是不能满足我的要求,为了能够适配更多的需求,我们需要在原有的基础上进行自定义控件。

今天我向大家介绍的就是android中最常见的刷新类控件。因为我们最近正在参加一个项目,在项目组长的带领下,我学到了很多的东西,这对我的android技术的提升非常大,定义一个自定义控件可能不是很难,但是如何让这个自定义控件更加有效、更加快速地运行。

首先我们需要建立一个自定义控件类:


package com.example.ui.widget;import com.example.androidtest.R;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.animation.Animation;import android.view.animation.LinearInterpolator;import android.view.animation.RotateAnimation;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ListAdapter;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;/** * 下拉刷新控件 *  * @author JeffLee  * @version 1.0 * @created 2014-10-21 */public class PullToRefreshListView extends ListView implements OnScrollListener {private final static String TAG = "PullToRefreshListView";private static final int MAX_Y_OVERSCROLL_DISTANCE = 100;private Context mContext;private int mMaxYOverscrollDistance;// 下拉刷新标志private final static int PULL_To_REFRESH = 0;// 松开刷新标志private final static int RELEASE_To_REFRESH = 1;// 正在刷新标志public final static int REFRESHING = 2;// 刷新完成标志private final static int DONE = 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 headContentOriginalTopPadding;private int startY;private int firstItemIndex;private int currentScrollState;private int state;private boolean isBack;public OnRefreshListener refreshListener;public OnLoadMoreListener loadMoreListener;// -- footer viewprivate PullToRefreshFooter mFooterView;private boolean mEnablePullLoad;private boolean mPullLoading = false;private boolean mIsFooterReady = false;private float mFirstY = -1; // save event yprivate boolean bloading = false;public PullToRefreshListView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public PullToRefreshListView(Context context, AttributeSet attrs,int defStyle) {super(context, attrs, defStyle);init(context);}public int getState() {return state;}private void init(Context context) {mContext = context;final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();final float density = metrics.density;mMaxYOverscrollDistance = (int) (density * MAX_Y_OVERSCROLL_DISTANCE);// 设置滑动效果animation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF, 0.5f);animation.setInterpolator(new LinearInterpolator());animation.setDuration(200);animation.setFillAfter(true);reverseAnimation = new RotateAnimation(-180, 0,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);reverseAnimation.setInterpolator(new LinearInterpolator());reverseAnimation.setDuration(200);reverseAnimation.setFillAfter(true);inflater = LayoutInflater.from(context);headView = (LinearLayout) inflater.inflate(R.layout.pull_to_refresh_head, null);arrowImageView = (ImageView) headView.findViewById(R.id.head_arrowImageView);// arrowImageView.setMinimumWidth(50);// 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);headContentOriginalTopPadding = headView.getPaddingTop();measureView(headView);headContentHeight = headView.getMeasuredHeight();headContentWidth = headView.getMeasuredWidth();headView.setPadding(headView.getPaddingLeft(), -1 * headContentHeight,headView.getPaddingRight(), headView.getPaddingBottom());headView.invalidate();addHeaderView(headView);// init footer viewmFooterView = new PullToRefreshFooter(context);setOnScrollListener(this);}@Overridepublic void setAdapter(ListAdapter adapter) {super.setAdapter(adapter);}// 需要有加载更多功能时候的设置adapter的方式public void setLoadMoreAdapter(ListAdapter adapter) {setAdapter(adapter);addFooterView();}public void addFooterView() {if (mIsFooterReady == false) {mIsFooterReady = true;mFooterView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 不在下拉刷新状态,以及没有全部加载,且不在加载更多的状态下进行点击刷新if (state != REFRESHING && !mPullLoading) { //startLoadMore();}}});addFooterView(mFooterView);}}/** * stop load more, reset footer view. */// public void stopLoadMore() {// if (mPullLoading == true) {// mPullLoading = false;// mFooterView.setState(XListViewFooter.STATE_NORMAL);// }// }public void completeLoadMore(int status) {if (mPullLoading == true) {state = PULL_To_REFRESH;mPullLoading = false;// mFooterView.setState(status);}mFooterView.setState(status);}public void setFooterState(int status) {mFooterView.setState(status);}public int getFooterState() {return mFooterView.getState();}private void startLoadMore() {// 设置了加载更多的监听器,以及加载更多状态不是数据加载完毕,和为空数据的时候,都可以进行上滑刷新if (loadMoreListener != null&& mFooterView.getState() != PullToRefreshFooter.STATE_LOADFULL&& mFooterView.getState() != PullToRefreshFooter.STATE_NULL) {mPullLoading = true;state = REFRESHING;mFooterView.setState(PullToRefreshFooter.STATE_LOADING);loadMoreListener.onLoadMore();}}public void settingOnScroll(AbsListView view, int firstVisiableItem,int visibleItemCount, int totalItemCount) {firstItemIndex = firstVisiableItem;// if (firstVisiableItem + visibleItemCount >=// totalItemCount){//currentScrollState == SCROLL_STATE_TOUCH_SCROLL// if (state != REFRESHING// && ! mPullLoading) {// startLoadMore();// }// }}public void settingOnScrollStateChanged(AbsListView view, int scrollState) {currentScrollState = scrollState;if (scrollState == SCROLL_STATE_IDLE && getCount() > 0) {// 判断滚动到底部if (view.getLastVisiblePosition() == (view.getCount() - 1)) {if (state != REFRESHING && !mPullLoading) {startLoadMore();}}}}@Overridepublic void onScroll(AbsListView view, int firstVisiableItem,int visibleItemCount, int totalItemCount) {settingOnScroll(view, firstVisiableItem, visibleItemCount,totalItemCount);}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {settingOnScrollStateChanged(view, scrollState);}public final boolean getLoadingState() {return bloading;}public final void setLoadingState(boolean bloading) {// Log.i(TAG, "setLoadingState bloading =" + bloading);this.bloading = bloading;}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mFirstY == -1) {mFirstY = event.getRawY();}switch (event.getAction()) {case MotionEvent.ACTION_DOWN:if (firstItemIndex == 0 && !isRecored) {startY = (int) event.getY();isRecored = true;}break;case MotionEvent.ACTION_CANCEL:// 失去焦点&取消动作case MotionEvent.ACTION_UP:if (state != REFRESHING) {if (state == DONE) { // 当前-抬起-ACTION_UP:DONE什么都不做} else if (state == PULL_To_REFRESH) { // 当前-抬起-ACTION_UP:PULL_To_REFRESH-->DONE-由下拉刷新状态到刷新完成状态state = DONE;changeHeaderViewByState();} else if (state == RELEASE_To_REFRESH) { // 当前-抬起-ACTION_UP:RELEASE_To_REFRESH-->REFRESHING-由松开刷新状态,到刷新完成状态state = REFRESHING;changeHeaderViewByState();onRefresh();}final float deltaY = event.getRawY() - mFirstY;if ((deltaY < 0) && !mPullLoading&& getLastVisiblePosition() == getCount() - 1) {startLoadMore();}mFirstY = -1; // reset}isRecored = false;isBack = false;break;case MotionEvent.ACTION_MOVE:int tempY = (int) event.getY();if (!isRecored && firstItemIndex == 0) {isRecored = true;startY = tempY;}// 如果正在加载,则不能进行刷新操作if (bloading) {break;}// Log.i(TAG,"tempY =" + tempY +";startY = "+startY);if (state != REFRESHING && isRecored) {// 可以松开刷新了if (state == RELEASE_To_REFRESH) {// 往上推,推到屏幕足够掩盖head的程度,但还没有全部掩盖if ((tempY - startY < headContentHeight + 20)&& (tempY - startY) > 0) {state = PULL_To_REFRESH;changeHeaderViewByState();} else if (tempY - startY <= 0) { // 一下子推到顶state = DONE;changeHeaderViewByState();} else { // 往下拉,或者还没有上推到屏幕顶部掩盖head// 不用进行特别的操作,只用更新paddingTop的值就行了}} else if (state == PULL_To_REFRESH) { // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态// 下拉到可以进入RELEASE_TO_REFRESH的状态if (tempY - startY >= headContentHeight + 20) {state = RELEASE_To_REFRESH;isBack = true;changeHeaderViewByState();}// 上推到顶了else if (tempY - startY <= 0) {state = DONE;changeHeaderViewByState();}} else if (state == DONE) { // done状态下if (tempY - startY > 0) {state = PULL_To_REFRESH;changeHeaderViewByState();}}// 更新headView的sizeif (state == PULL_To_REFRESH) {int topPadding = ((-1 * headContentHeight + (tempY - startY)));headView.setPadding(headView.getPaddingLeft(), topPadding,headView.getPaddingRight(),headView.getPaddingBottom());headView.invalidate();}// 更新headView的paddingTopif (state == RELEASE_To_REFRESH) {int topPadding = ((tempY - startY - headContentHeight));if (topPadding > mMaxYOverscrollDistance)topPadding = mMaxYOverscrollDistance;headView.setPadding(headView.getPaddingLeft(), topPadding,headView.getPaddingRight(),headView.getPaddingBottom());headView.invalidate();}}break;default:break;}if (state == RELEASE_To_REFRESH) {return true;}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(R.string.pull_to_refresh_release_label);break;case PULL_To_REFRESH:progressBar.setVisibility(View.GONE);tipsTextview.setVisibility(View.VISIBLE);lastUpdatedTextView.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.setVisibility(View.VISIBLE);if (isBack) {isBack = false;arrowImageView.clearAnimation();arrowImageView.startAnimation(reverseAnimation);}tipsTextview.setText(R.string.pull_to_refresh_pull_label);break;case REFRESHING:headView.setPadding(headView.getPaddingLeft(),headContentOriginalTopPadding, headView.getPaddingRight(),headView.getPaddingBottom());headView.invalidate();progressBar.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.setVisibility(View.GONE);tipsTextview.setText(R.string.pull_to_refresh_refreshing_label);lastUpdatedTextView.setVisibility(View.GONE);break;case DONE: {headView.setPadding(headView.getPaddingLeft(), -1* headContentHeight, headView.getPaddingRight(),headView.getPaddingBottom());progressBar.setVisibility(View.GONE);arrowImageView.clearAnimation();// 此处更换图标// arrowImageView.setImageResource(R.drawable.ic_pulltorefresh_arrow);Bitmap arrow = BitmapFactory.decodeResource(getResources(),R.drawable.ic_pulltorefresh_arrow);Drawable background = new BitmapDrawable(arrow);arrowImageView.setBackgroundDrawable(background);tipsTextview.setText(R.string.pull_to_refresh_pull_label);lastUpdatedTextView.setVisibility(View.VISIBLE);headView.invalidate();}break;}}// 点击刷新public void clickRefresh() {setSelection(0);state = REFRESHING;changeHeaderViewByState();onRefresh();}public void setOnRefreshListener(OnRefreshListener refreshListener) {this.refreshListener = refreshListener;}public void setOnLoadMoreListener(OnLoadMoreListener loadMoreListener) {this.loadMoreListener = loadMoreListener;}public interface OnRefreshListener {public void onRefresh();}public interface OnLoadMoreListener {public void onLoadMore();}public void onRefreshComplete(String update) {lastUpdatedTextView.setText(update);onRefreshComplete();}public void firstRefreshing() {state = REFRESHING;headView.setPadding(0, 10, 0, 0);progressBar.setVisibility(View.VISIBLE);arrowImageView.clearAnimation();arrowImageView.setVisibility(View.GONE);tipsTextview.setText(R.string.pull_to_refresh_refreshing_label);lastUpdatedTextView.setVisibility(View.GONE);}/** * stop refresh, reset header view. */public void stopRefresh() {state = PULL_To_REFRESH;changeHeaderViewByState();}public void onRefreshComplete() {state = DONE;changeHeaderViewByState();setSelection(0);}public void onRefreshComplete(int loadMoreStatus) {onRefreshComplete();mFooterView.setState(loadMoreStatus);}private void onRefresh() {if (refreshListener != null) {refreshListener.onRefresh();}}// 计算headView的width及height值private 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);}/* * @Override protected boolean overScrollBy(int deltaX, int deltaY, int * scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int * maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { // This is * where the magic happens, we have replaced the incoming // maxOverScrollY * with our own custom variable mMaxYOverscrollDistance; *  * return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, * scrollRangeY, maxOverScrollX, mMaxYOverscrollDistance, isTouchEvent); } */}
下面是pull_to_refresh_head.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:clickable="true"    android:focusable="false"    android:orientation="vertical" >    <RelativeLayout        android:id="@+id/head_contentLayout"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:paddingBottom="10dip"        android:paddingTop="10dip" >        <ImageView            android:id="@+id/head_arrowImageView"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignParentTop="true"            android:layout_centerVertical="true"            android:layout_marginLeft="50dp"            android:background="@drawable/ic_pulltorefresh_arrow" />        <FrameLayout            android:layout_width="wrap_content"            android:layout_height="fill_parent"            android:layout_alignParentLeft="true"            android:layout_centerVertical="true"            android:layout_marginLeft="100dip"            android:layout_marginRight="10dip"            android:paddingBottom="10dip"            android:paddingTop="10dip" >            <ProgressBar                android:id="@+id/head_progressBar"                style="@style/loading_small"                android:visibility="gone" />        </FrameLayout>        <LinearLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="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/list_title" />            <TextView                android:id="@+id/head_lastUpdatedTextView"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:textColor="@color/list_title"                android:textSize="10sp"                android:visibility="gone" />        </LinearLayout>    </RelativeLayout></LinearLayout>


package com.example.ui.widget;import com.example.androidtest.R;import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.widget.RelativeLayout;import android.widget.TextView;public class PullToRefreshFooter extends RelativeLayout {public final static int STATE_NORMAL = 0; //正常状态,还有更多需要加载public final static int STATE_READY = 1;public final static int STATE_LOADING = 2; //正在加载中public final static int STATE_LOADFULL = 3;//数据全部加载完public final static int STATE_NULL = 4;//没有数据,初始状态private Context mContext;private View mContentView;private View mProgressBar;private TextView mHintView;private int state;public PullToRefreshFooter(Context context) {super(context);initView(context);setVisibility(View.GONE);}public PullToRefreshFooter(Context context, AttributeSet attrs) {super(context, attrs);initView(context);}public void setState(int state) {this.state = state;//mHintView.setVisibility(View.INVISIBLE);//mProgressBar.setVisibility(View.INVISIBLE);//mHintView.setVisibility(View.INVISIBLE);mProgressBar.setVisibility(View.GONE);mHintView.setVisibility(View.VISIBLE);setVisibility(View.VISIBLE);if (state == STATE_READY) {mHintView.setText(R.string.load_ready);} else if (state == STATE_LOADING) {mProgressBar.setVisibility(View.VISIBLE);mHintView.setText(R.string.load_ing);} else if (state == STATE_LOADFULL){mHintView.setText(R.string.load_full);} else if (state == STATE_NULL) {mHintView.setText(R.string.load_empty);//setVisibility(View.GONE);} else {mHintView.setText(R.string.load_more);}}public int getState() {return state;}public void setBottomMargin(int height) {if (height < 0) return ;RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mContentView.getLayoutParams();lp.bottomMargin = height;mContentView.setLayoutParams(lp);}public int getBottomMargin() {RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mContentView.getLayoutParams();return lp.bottomMargin;}/** * normal status */public void normal() {mHintView.setVisibility(View.VISIBLE);mProgressBar.setVisibility(View.GONE);}/** * loading status  */public void loading() {mHintView.setVisibility(View.GONE);mProgressBar.setVisibility(View.VISIBLE);}/** * hide footer when disable pull load more */public void hide() {RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mContentView.getLayoutParams();lp.height = 0;mContentView.setLayoutParams(lp);}/** * show footer */public void show() {RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)mContentView.getLayoutParams();lp.height = android.view.ViewGroup.LayoutParams.WRAP_CONTENT;mContentView.setLayoutParams(lp);}private void initView(Context context) {mContext = context;RelativeLayout moreView = (RelativeLayout)LayoutInflater.from(mContext).inflate(R.layout.listview_footer, null);addView(moreView);moreView.setLayoutParams(new RelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT));mContentView = moreView.findViewById(R.id.listview_foot_content);mProgressBar = moreView.findViewById(R.id.listview_foot_progress);mHintView = (TextView)moreView.findViewById(R.id.listview_foot_more);}}

下面是listview_footer.xml文件:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/listview_foot_content"    android:orientation="horizontal"    android:layout_width="fill_parent"android:layout_height="wrap_content"    android:gravity="center"    android:padding="3dp"    android:clickable="true"    android:focusable="false">    <TextView android:id="@+id/listview_foot_more"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerInParent="true"        android:padding="5dp"        android:textColor="@color/list_subTitle"        android:text="@string/load_empty"/>    <ProgressBar android:id="@+id/listview_foot_progress"android:layout_toLeftOf="@id/listview_foot_more"android:layout_centerVertical="true"style="@style/loading_small"/></RelativeLayout>

然后我们就可以使用这个刷新类的自定义控件了,

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical"    tools:context=".PullRefreshActivity" >    <com.example.ui.widget.PullToRefreshListView        android:id="@+id/listview"        style="@style/common_listview"        android:layout_width="match_parent"        android:divider="@drawable/divided_line"        android:dividerHeight="1dp"        android:listSelector="@drawable/listview_item_nocolor_selector"        android:paddingLeft="8dp"        android:paddingRight="8dp"        android:scrollbars="none" >    </com.example.ui.widget.PullToRefreshListView></LinearLayout>

然后再Activity中加载这个控件:

package com.example.androidtest;import com.example.ui.adapter.PullRefreshAdapter;import com.example.ui.widget.PullToRefreshListView;import com.example.ui.widget.PullToRefreshListView.OnRefreshListener;import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.widget.Toast;public class PullRefreshActivity extends Activity {private Context mContext;private PullRefreshAdapter pullRefreshAdapter;private PullToRefreshListView listView;@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.activity_pulltorefresh);initView();initListView();}private void initView() {mContext = this;listView = (PullToRefreshListView) findViewById(R.id.listview);}private void initListView() {pullRefreshAdapter = new PullRefreshAdapter(mContext);listView.setLoadMoreAdapter(pullRefreshAdapter);listView.setOnRefreshListener(new OnRefreshListener() {@Overridepublic void onRefresh() {// TODO Auto-generated method stubToast.makeText(mContext, "哈哈", Toast.LENGTH_SHORT).show();}});}}

package com.example.ui.adapter;import android.content.Context;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;public class PullRefreshAdapter extends BaseAdapter {private Context mContext;public PullRefreshAdapter(Context context) {super();this.mContext = context;}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn 0;}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn null;}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubreturn null;}}

得到的效果如下:




上面显示出来了加载刷新的功能,可以在需要刷新的时候显示这个控件,这样对UI显示地比较方面。由于时间比较仓促,暂时先放上源码,以后再好好分析源码,请大家多多指教。

0 0