软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 移动开发 -> 【android】自定义组合控件PullToRefreshRecyeclerView -> 正文阅读

[移动开发]【android】自定义组合控件PullToRefreshRecyeclerView


场景:自从Android 5.0发布以来,越来越多的开发者开始接触RecyeclerView,但是RecyclerView如何实现下拉刷新,上拉加在更多。于是我就偷懒 写了一个,以供大家参考和学习,以待大家改进。
[img]http://img.blog.csdn.net/20150331164652043?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFib255/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast



构思:想必大家对SwipeRefreshLayout这个控件有一定了解,没错本次自定义组合控件也就是SwipeRefreshLayout与RecyeclerView的组合。
那么我们一步一步来实现:
1.首先写一个组合布局如下:pulltorefreshrecyclerview.xml

<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:background="@android:color/white"
    android:orientation="vertical" >


    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/all_swipe"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical" />
    </android.support.v4.widget.SwipeRefreshLayout>

</LinearLayout>
2.声明一个类PulltoRefreshRecyclerView.java继承自LinearLayout
a.声明一个接口

public interface RefreshLoadMoreListener {
		public void onRefresh();

		public void onLoadMore();
	}
在Activity或者Fragment中去实现PulltoRefreshRecyclerView.RefreshLoadMoreListener接口
PulltoRefreshRecyclerView调用刷新和加在更多,实质上是回调Activity或者Fragment实现的接口方法。
b.即 在PulltoRefreshRecyclerView有loadMore和refresh方法

public void loadMore() {
		if (mRefreshLoadMoreListner != null && hasMore) {
			mRefreshLoadMoreListner.onLoadMore();
			
		}
	}
public void refresh() {
		if (mRefreshLoadMoreListner != null) {
			mRefreshLoadMoreListner.onRefresh();
		}
	}
c.那么就需要拿到这个实现的刷新加载监听实例
PulltoRefreshRecyclerView实现一个设置监听的方法

public void setRefreshLoadMoreListener(RefreshLoadMoreListener listener) {
		mRefreshLoadMoreListner = listener;
	}

d.RecyeclerView是基于Adapter的开发,那么这里也需要可以设置Adapter

public void setAdapter(RecyclerView.Adapter adapter) {
		if (adapter != null)
			recyclerView.setAdapter(adapter);
	}

e.考虑一下特殊的需求,有时禁止刷新,有时禁止加载更多那么我们来完善一下

public void setPullLoadMoreEnable(boolean enable) {
		this.hasMore = enable;
	}

public boolean getPullLoadMoreEnable() {
		return hasMore;
	}

public void setPullRefreshEnable(boolean enable) {
		swipeRfl.setEnabled(enable);
	}

public boolean getPullRefreshEnable() {
		return swipeRfl.isEnabled();
	}
只要对相应的enable传入true或false则可进行开关控制
f.RecyeclerView可以是水平也可以是垂直,把这项功能也加入一下默认我们就让它为垂直
[img]http://img.blog.csdn.net/20150331164838729?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFib255/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast


public void setOrientation(int orientation) {
		if (orientation != 0 && orientation != 1) {
			layoutManager.setOrientation(VERTICAL);
		} else {
			layoutManager.setOrientation(HORIZONTAL);
		}
	}

public int getOrientation() {
		return layoutManager.getOrientation();
	}

7.SwipeRefreshLayout的停止刷新效果

public void stopRefresh() {
		isRefresh = false;
		swipeRfl.setRefreshing(false);
	}

g.刷新原理和加载更多原理
刷新实际上是添加SwipeRefreshLayout的OnRefreshListener监听,在符合!isRefresh的前提下回调RefreshLoadMoreListener的onRefresh方法

加载更多原理是添加RecyeclerView的OnScrollListener监听,在符合hasMore且显示最后一项的前提下回调
RefreshLoadMoreListener的onLoadMore方法

[img]http://img.blog.csdn.net/20150331164825531?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFib255/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast



2.那么我们来看看具体的PulltoRefreshRecyclerView代码

package com.jabony.recyclerpulltorefresh;

import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
/**
 * 
 * @author jabony
 * @since 2015年3月31日 16:06:59
 *
 */
public class PulltoRefreshRecyclerView extends LinearLayout {
	/**
	 * 垂直方向
	 */
	static final int VERTICAL = LinearLayoutManager.VERTICAL;
	/**
	 * 水平方向
	 */
	static final int HORIZONTAL = LinearLayoutManager.HORIZONTAL;
	/**
	 * 内容控件
	 */
	private RecyclerView recyclerView;
	/**
	 * 刷新布局控件
	 */
	private SwipeRefreshLayout swipeRfl;
	private LinearLayoutManager layoutManager;
	/*
	 * 刷新布局的监听
	 */
	private OnRefreshListener mRefreshListener;
	/**
	 * 内容控件滚动监听 
	 */
	private RecyclerView.OnScrollListener mScrollListener;
	/**
	 * 内容适配器
	 */
	private RecyclerView.Adapter mAdapter;
	/*
	 * 刷新加载监听事件
	 */
	private RefreshLoadMoreListener mRefreshLoadMoreListner;
	/**
	 * 是否可以加载更多
	 */
	private boolean hasMore = true;
	/**
	 * 是否正在刷新
	 */
	private boolean isRefresh = false;
	/**
	 * 是否正在加载更多
	 */
	private boolean isLoadMore = false;

	public PulltoRefreshRecyclerView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	@SuppressWarnings("deprecation")
	public PulltoRefreshRecyclerView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// 导入布局
		LayoutInflater.from(context).inflate(
				R.layout.pulltorefreshrecyclerview, this, true);
		swipeRfl = (SwipeRefreshLayout) findViewById(R.id.all_swipe);
		recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
		// 加载颜色是循环播放的,只要没有完成刷新就会一直循环,color1>color2>color3>color4
		// swipeRfl.setColorScheme(Color.BLUE, Color.GREEN, Color.RED,
		// Color.YELLOW);

		/**
		 * 监听上拉至底部滚动监听
		 */
		mScrollListener = new RecyclerView.OnScrollListener() {
			@Override
			public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
				super.onScrolled(recyclerView, dx, dy);
				//最后显示的项
				int lastVisibleItem = layoutManager
						.findLastVisibleItemPosition();
				int totalItemCount = layoutManager.getItemCount();
				// lastVisibleItem >= totalItemCount - 4 表示剩下4个item自动加载
				// dy>0 表示向下滑动
				/*	if (hasMore && (lastVisibleItem >= totalItemCount - 1)
						&& dy > 0 && !isLoadMore) {
					isLoadMore = true;
					loadMore();
				}*/
				/**
				 * 无论水平还是垂直
				 */
				if (hasMore && (lastVisibleItem >= totalItemCount - 1)
						&& !isLoadMore) {
					isLoadMore = true;
					loadMore();
				}

			}
		};

		/**
		 * 下拉至顶部刷新监听
		 */
		mRefreshListener = new OnRefreshListener() {

			@Override
			public void onRefresh() {
				if (!isRefresh) {
					isRefresh = true;
					refresh();
				}
			}
		};
		swipeRfl.setOnRefreshListener(mRefreshListener);
		recyclerView.setHasFixedSize(true);
		layoutManager = new LinearLayoutManager(context);
		layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
		recyclerView.setLayoutManager(layoutManager);
		recyclerView.setOnScrollListener(mScrollListener);
	}

	public void setOrientation(int orientation) {
		if (orientation != 0 && orientation != 1) {
			layoutManager.setOrientation(VERTICAL);
		} else {
			layoutManager.setOrientation(HORIZONTAL);
		}
	}

	public int getOrientation() {
		return layoutManager.getOrientation();
	}

	public void setPullLoadMoreEnable(boolean enable) {
		this.hasMore = enable;
	}

	public boolean getPullLoadMoreEnable() {
		return hasMore;
	}

	public void setPullRefreshEnable(boolean enable) {
		swipeRfl.setEnabled(enable);
	}

	public boolean getPullRefreshEnable() {
		return swipeRfl.isEnabled();
	}

	public void setLoadMoreListener() {
		recyclerView.setOnScrollListener(mScrollListener);
	}

	public void loadMore() {
		if (mRefreshLoadMoreListner != null && hasMore) {
			mRefreshLoadMoreListner.onLoadMore();
			
		}
	}

	/**
	 * 加载更多完毕,为防止频繁网络请求,isLoadMore为false才可再次请求更多数据
	 */
	public void setLoadMoreCompleted(){
		isLoadMore = false;
	}
	
	public void stopRefresh() {
		isRefresh = false;
		swipeRfl.setRefreshing(false);
	}

	public void setRefreshLoadMoreListener(RefreshLoadMoreListener listener) {
		mRefreshLoadMoreListner = listener;
	}

	public void refresh() {
		if (mRefreshLoadMoreListner != null) {
			mRefreshLoadMoreListner.onRefresh();
		}
	}

	public void setAdapter(RecyclerView.Adapter adapter) {
		if (adapter != null)
			recyclerView.setAdapter(adapter);
	}

	public interface RefreshLoadMoreListener {
		public void onRefresh();

		public void onLoadMore();
	}
}

3.那么我们来看看如何使用

package com.example.pulltorefreshrecyeclerviewdemo;

import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import com.jabony.recyclerpulltorefresh.PullRefreshRecyclerView;

/**
 * 
 * @author jabony
 * @since 2015年3月31日 16:08:56
 */
public class MainActivity extends Activity implements
		PullRefreshRecyclerView.RefreshLoadMoreListener {
	private PullRefreshRecyclerView prrv;
	private ActivityAdapter allActAdapter;
	private JSONObject response = null;
	private int page = 0;
	/**
	 * 全部活动数据源
	 */
	private ArrayList<ActivityBean> actAllList = new ArrayList<ActivityBean>();
	private String jsonStr = "{\"datas\":{\"count\":\"6\",\"list\":[{\"id\":\"311\",\"end_date\":\"1437235200\",\"shopid\":\"387\",\"subject\":\"金鹏会员里程百日冲刺,最高可获35000里程奖励\",\"is_collected\":\"2\",img:\"http://jfgj.wadiankeji.com/Uploads/2015-03-26/5513bd4120d64.jpg\",\"collec_num\":\"1\",\"start_date\":\"1428681600\",\"url\":\"http://jfgj.wadiankeji.com/api/html/detail/actid/311\"},{\"id\":\"302\",\"end_date\":\"1437235200\",\"shopid\":\"387\",\"subject\":\"金鹏会员里程百日冲刺,最高可获35000里程奖励\",\"is_collected\":\"2\",img:\"http://jfgj.wadiankeji.com/Uploads/2015-03-26/5513bd4120d64.jpg\",\"collec_num\":\"1\",\"start_date\":\"1428681600\",\"url\":\"http://jfgj.wadiankeji.com/api/html/detail/actid/311\"},{\"id\":\"311\",\"end_date\":\"1437235200\",\"shopid\":\"387\",\"subject\":\"金鹏会员里程百日冲刺,最高可获35000里程奖励\",\"is_collected\":\"2\",img:\"http://jfgj.wadiankeji.com/Uploads/2015-03-26/5513bd4120d64.jpg\",\"collec_num\":\"1\",\"start_date\":\"1428681600\",\"url\":\"http://jfgj.wadiankeji.com/api/html/detail/actid/311\"},{\"id\":\"311\",\"end_date\":\"1437235200\",\"shopid\":\"387\",\"subject\":\"金鹏会员里程百日冲刺,最高可获35000里程奖励\",\"is_collected\":\"2\",img:\"http://jfgj.wadiankeji.com/Uploads/2015-03-26/5513bd4120d64.jpg\",\"collec_num\":\"1\",\"start_date\":\"1428681600\",\"url\":\"http://jfgj.wadiankeji.com/api/html/detail/actid/311\"},{\"id\":\"311\",\"end_date\":\"1437235200\",\"shopid\":\"387\",\"subject\":\"金鹏会员里程百日冲刺,最高可获35000里程奖励\",\"is_collected\":\"2\",img:\"http://jfgj.wadiankeji.com/Uploads/2015-03-26/5513bd4120d64.jpg\",\"collec_num\":\"1\",\"start_date\":\"1428681600\",\"url\":\"http://jfgj.wadiankeji.com/api/html/detail/actid/311\"},{\"id\":\"311\",\"end_date\":\"1437235200\",\"shopid\":\"387\",\"subject\":\"金鹏会员里程百日冲刺,最高可获35000里程奖励\",\"is_collected\":\"2\",img:\"http://jfgj.wadiankeji.com/Uploads/2015-03-26/5513bd4120d64.jpg\",\"collec_num\":\"1\",\"start_date\":\"1428681600\",\"url\":\"http://jfgj.wadiankeji.com/api/html/detail/actid/311\"}]},\"status\":\"10000\",\"msg\":\"faxian\"}";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		/**
		 * 绑定组合控件
		 */
		prrv = (PullRefreshRecyclerView) findViewById(R.id.prrv);
		/**
		 * 初始化适配器
		 */
		allActAdapter = new ActivityAdapter(actAllList, MainActivity.this);
		/**
		 * 下拉上拉加载更多监听
		 */
		prrv.setRefreshLoadMoreListener(this);
		/**
		 * 禁用刷新
		 */
		// prrv.setPullRefreshEnable(false);
		/**
		 * 禁用加载更多
		 */
		// prrv.setPullLoadMoreEnable(false);

		/**
		 * 设置布局方向
		 */
		prrv.setVertical();

		/**
		 * 设置适配器
		 */
		prrv.setAdapter(allActAdapter);
		/**
		 * 首次进入刷新
		 */
		prrv.refresh();
	}

	/**
	 * 加载分页数据
	 */
	public void loadNetDatas(int currentPage) {
		if (currentPage > 2) {
			prrv.setPullLoadMoreEnable(false);
			return;
		}
		try {
			response = new JSONObject(jsonStr);
			String msg = null;
			int count = 0;
			int status = 0;
			if (!response.isNull("datas")) {
				JSONObject dataObj = response.getJSONObject("datas");

				if (!dataObj.isNull("count")) {
					count = dataObj.getInt("count");
				}

				if (!dataObj.isNull("list")) {
					JSONArray listArray = dataObj.getJSONArray("list");
					int size = listArray.length();
					for (int i = 0; i < size; i++) {

						JSONObject obj = listArray.getJSONObject(i);
						ActivityBean ab = new ActivityBean();
						if (!obj.isNull("id")) {
							ab.setActId(obj.getString("id"));
						}
						if (!obj.isNull("shopid")) {
							ab.setShopId(obj.getString("shopid"));
						}
						if (!obj.isNull("is_top")) {
							if ("1".equals(obj.getString("is_top"))) {
								ab.setTop(true);
							}

						}

						if (!obj.isNull("img")) {
							ab.setImgUrl(obj.getString("img"));
						}
						if (!obj.isNull("subject")) {
							ab.setActTitile(obj.getString("subject"));
						}
						if (!obj.isNull("start_date")) {
							if (obj.getString("start_date") != null
									&& !"".equals(obj.getString("start_date"))) {
								ab.setStartDate(obj.getLong("start_date"));
							}

						}
						if (!obj.isNull("end_date")) {
							if (obj.getString("end_date") != null
									&& !"".equals(obj.getString("end_date"))) {
								ab.setEndDate(obj.getLong("end_date"));
							}

						}
						if (!obj.isNull("collec_num")) {
							String collcNum = obj.getString("collec_num");
							if (collcNum == null || "".equals(collcNum)) {
								ab.setCollectNum(0);
							} else {
								ab.setCollectNum(obj.getInt("collec_num"));
							}
							collcNum = null;
						}
						if (!obj.isNull("is_collected")) {
							ab.setCollected(obj.getInt("is_collected") == 1 ? true
									: false);
						}
						if (!obj.isNull("url")) {
							ab.setUrl(obj.getString("url"));
						}
						actAllList.add(ab);
						/**
						 * 刷新适配器
						 */
						allActAdapter.notifyDataSetChanged();
						/**
						 * 如果刷新则停止刷新
						 */
						prrv.stopRefresh();
						/**
						 * 加载更多完毕
						 */
						prrv.setLoadMoreCompleted();
						/**
						 * 如果没有更多数据则设置不可加载更多
						 */
						// prrv.setPullLoadMoreEnable(false);
					}
				}
			}
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void onRefresh() {
		// TODO Auto-generated method stub
		prrv.setPullRefreshEnable(true);
		prrv.setPullLoadMoreEnable(true);
		actAllList.clear();
		page = 1;
		loadNetDatas(page);
	}

	@Override
	public void onLoadMore() {
		// TODO Auto-generated method stub
		page++;
		loadNetDatas(page);
	}

	@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;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_delete) {
			actAllList.clear();
			allActAdapter.notifyDataSetChanged();
			page = 1;
			prrv.customExceptView(R.drawable.no_data, "这里空空如也");
			return true;
		}
		if (id == R.id.action_orintation) {
			int orientation = prrv.getOrientation();
			if (orientation == LinearLayout.HORIZONTAL) {
				prrv.setVertical();
			} else {
				prrv.setHorizontal();
			}
			return true;
		}
		if (id == R.id.action_refreshable) {
			if (prrv.getPullRefreshEnable()) {
				prrv.setPullRefreshEnable(!prrv.getPullRefreshEnable());
			} else {
				prrv.setPullRefreshEnable(!prrv.getPullRefreshEnable());
			}
			return true;
		}
		if (id == R.id.action_loadmoreable) {
			if (prrv.getPullLoadMoreEnable()) {
				prrv.setPullLoadMoreEnable(!prrv.getPullLoadMoreEnable());
			} else {
				prrv.setPullLoadMoreEnable(!prrv.getPullLoadMoreEnable());
			}
			return true;
		}
		if (id == R.id.action_stoprefresh) {
			prrv.stopRefresh();
			return true;
		}
		if (id == R.id.action_scrollToTop) {
			prrv.scrollToTop();
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}

4.看上去已经搞定了,但你会发现需要引入一个库项目没错RecyclerView的库项目
这都好办,我们考虑的重点不在这里,这样就完美了吗?我们在使用的时候在布局中这么写,看是没有什么

<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="com.jabony.recyclerpulltorefresh.MainActivity" >
    <com.jabony.recyclerpulltorefresh.PullRefreshRecyclerView
        android:id="@+id/prrv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

5.我们能不能把自定义的View布局省掉?
答案当然是肯定的:我们来做个小实验在原来采用
// 导入布局
LayoutInflater.from(context).inflate(R.layout.pulltorefreshrecyclerview, this, true);

在老的方式做下调整,完全脱离布局,代码写布局

		LinearLayout rootLl = new LinearLayout(context);
		rootLl.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT));
		<span style="color:#ff0000;">mExceptView = initExceptionView(context);//异常布局</span>
		mExceptView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT));
		mExceptView.setVisibility(View.GONE);
		swipeRfl = new SwipeRefreshLayout(context);
		swipeRfl.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.MATCH_PARENT));
		FrameLayout bootLl = new FrameLayout(context);
		bootLl.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.WRAP_CONTENT));
		recyclerView = new RecyclerView(context);
		recyclerView.setVerticalScrollBarEnabled(true);
		recyclerView.setHorizontalScrollBarEnabled(true);
		recyclerView.setLayoutParams(new LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
		bootLl.addView(recyclerView);
		bootLl.addView(mExceptView);
		swipeRfl.addView(bootLl);
		rootLl.addView(swipeRfl);
		this.addView(rootLl);
这样就把需要有xml布局引入的布局改动为纯代码的,虽然不怎么完美,但是测试使用是没有问题了。
6.没错我引入了异常布局,为了交互友好,允许用户半自定义异常界面
[img]http://img.blog.csdn.net/20150331164845577?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamFib255/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast

也就是其实布局什么样子 我早就定好了,你只需要按照方法传图片的id和提示的字符串,你一定会好奇到底是什么样子的,马上贴图。
那么刷新呢,非常简单,点击图片可以刷新操作,这样就看似比较完善了。


7.总结一下:
PulltoRefreshRecyclerView是在SwipeRefreshLayout和RecyclerView的基础上组合改造的。
需要实现OnRefreshListener的方法
可以控制能否刷新,能否加在更多
可以控制显示的方向
提供回到Top的方法
停止刷新效果
可以半定义异常界面的效果
下载地址:

自定义组合下拉刷新上拉加载更多控件




......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2015-04-01 00:53:43  
移动开发 最新文章
深入了解android中的消息机制Handler
Android
Libgdx之BitmapFont字体
AndroidApp发布到应用市场的流程
Android开发找工作之前先看看这些知识点吧
View的事件分发机制解析
简单介绍了解白鹭引擎Egret
Cocos2d
android获取本地图片(二)
动画特效七:碰撞动画
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2017年9日历
2017-9-24 16:49:05
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --