前几天心血来潮,打算根据看知乎的API自己做一个小知乎,定制的过程遇到ListView的优化问题及图片未缓存重加载等等许多问题,解决了以后打算和博友分享一下。
接口数据:http://api.kanzhihu.com/getpostanswers/20150925/archive
首先,Json数据太常用,相信每一位开发者Json的解析都是必备的。我们要准备以下知识:
JavaBean,枚举你需要的元素,用来存储数据。
异步加载网络内容的必备途径,多线程加载+AsyncTask两种方式。
Json解析的常见方法,JsonObject和JsonArray;
ListView优化,使用Viewholder
下面上代码,侧边栏这里就不说了,相信大家都会。
layout_main.xml
图文混排ListView的item文件:
item_id_listview.xml
JsonBean.java 这儿是javabean,用来存储需要的数据对象
public class JsonBean {
public String name;
public String title;
public String body;
public String time;
public String url;
}
CommentListView.java 重写ListView禁止滑动,避免与ScrollView发生冲突:
public class CommentListView extends ListView {
public CommentListView(Context context) {
super(context);
}
public CommentListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CommentListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public CommentListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
int mExpandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, mExpandSpec);
}
}
ListAdapter.java
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.ynu.commando.Imageloader;
import com.ynu.commando.JavaBean.JsonBean;
import com.ynu.commando.R;
import com.ynu.commando.UsingTest;
import java.util.List;
/**
* Created by 江树金 on 2016/4/23.
*/
public class ListAdapter extends BaseAdapter {
private List mlist;//JsonBean的list
private LayoutInflater mInflater;
private Imageloader mImageloader;
private Context context;
public void setContext(Context context) {
this.context = context;
}
//将数据映射过来
public ListAdapter(Context context, List data){
mlist=data;
mInflater= LayoutInflater.from(context);
mImageloader=new Imageloader();
this.context=context;
}
@Override
public int getCount() {
return mlist.size();
}
@Override
public Object getItem(int position) {
return mlist.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder=null;
if (viewHolder==null){
viewHolder=new ViewHolder();
convertView=mInflater.inflate(R.layout.item_id_listview,null);
viewHolder.img= (ImageView) convertView.findViewById(R.id.img);
viewHolder.img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("========点击了头像========", "点击事件启动-------------- ");
context.startActivity(new Intent(context, UsingTest.class));
}
});
viewHolder.title= (TextView) convertView.findViewById(R.id.title);
viewHolder.body= (TextView) convertView.findViewById(R.id.body);
viewHolder.name= (TextView) convertView.findViewById(R.id.name);
viewHolder.time= (TextView) convertView.findViewById(R.id.time);
convertView.setTag(viewHolder);
}else {
viewHolder= (ViewHolder) convertView.getTag();
}
String url=mlist.get(position).url;
viewHolder.img.setTag(url);//进行绑定,为了在imageLoader中的语句,
/*
* @二选一进行图片加载
* */
/*mImageloader.showImagerByThread(viewHolder.img,mlist.get(position).url);//使用多线程的方法加载图片*/
mImageloader.showImageByAsyncTask(viewHolder.img,url);//使用AsyncTask的方式加载
viewHolder.title.setText(mlist.get(position).title);//从JsonBean中取出元素设置给viewholder的元素
viewHolder.body.setText(mlist.get(position).body);
viewHolder.name.setText(mlist.get(position).name);
viewHolder.time.setText(mlist.get(position).time);
return convertView;
}
class ViewHolder{
public TextView title;
public TextView body;
public TextView name;
public TextView time;
public ImageView img;
}
}
Imageloader.java
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.nfc.Tag;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by 江树金 on 2016/4/23.
*/
public class Imageloader {
//注:异步加载图片可以使用①多线程加载 或者 ②AsyncTask去加载。[个人感觉多线程的加载速度大于AsyncTask]
private ImageView mimageView;
private String mUrl;
//创建cache
private LruCache mCache;
public void showImagerByThread(ImageView imageView, final String url){
mimageView=imageView;
mUrl=url;
new Thread(new Runnable() {//多线程加载
@Override
public void run() {
Bitmap bitmap=getBitmapFromUrl(url);
Message msg=Message.obtain();
msg.obj=bitmap;
handler.sendMessage(msg);
}
}).start();
}
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//在handler中处理UI
//避免缓存的图片对正确图片的影响,viewholder会重新加载设定图片
if (mimageView.getTag().equals(mUrl)){
mimageView.setImageBitmap((Bitmap) msg.obj);//避免影响之后再去加载bitmap
}
}
};
/*
* 公用方法 获取URL并存为bitMap类型 进行加载时可选择多线程或者AsyncTask进行加载
* */
public Bitmap getBitmapFromUrl(String UrlString){
Bitmap bitmap;
InputStream is;
try {
URL url=new URL(UrlString);
HttpURLConnection connection= (HttpURLConnection) url.openConnection();
is=new BufferedInputStream(connection.getInputStream());//获取InputStream对象
bitmap= BitmapFactory.decodeStream(is);
//资源释放,优化作用
connection.disconnect();
return bitmap;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void showImageByAsyncTask(ImageView imageView,String url){
//从缓存中取出对应的图片
Bitmap bitmap=getBitmapFromCache(url);
if (bitmap==null){
//说明内存中没有该图片 只能从网络中获取图片
new newAsyncTask(imageView,url).execute(url);//将url传递到newAsyncTask中处理。
}else {
//如果有就直接使用该图片
imageView.setImageBitmap(bitmap);
}
}
private class newAsyncTask extends AsyncTask{
private ImageView mImageView;
private String murl;
@Override
protected Bitmap doInBackground(String... params) {//完成异步加载任务
//先进行下载,在判断缓存中有没有
String url=params[0];
Bitmap bitmap=getBitmapFromUrl(url);
if (bitmap!=null){
//加入缓存
addBitmapToCache(url,bitmap);
}
return bitmap;
}
public newAsyncTask(ImageView imageView,String url){
mImageView=imageView;
murl=url;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (mImageView.getTag().equals(murl)){
mImageView.setImageBitmap(bitmap);
}
}
}
public Imageloader(){
/*
*设定一部分内存转化为我们的缓存空间.Lru算法
* */
int MaxMemory= (int) Runtime.getRuntime().maxMemory();
int cacheSize=MaxMemory/4;
mCache=new LruCache(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
//返回实际大小
return value.getByteCount();//在每次存入缓存的时候调用该方法,告诉当前系统存入的对象有多大
}
};
}
/*
* 将url增加到缓存
* 在增加之前校验缓存是否存在
* */
public void addBitmapToCache(String url,Bitmap bitmap){
if (getBitmapFromCache(url)==null){
mCache.put(url,bitmap);
}
}
public Bitmap getBitmapFromCache(String url){//通过url去返回指定的cache
return mCache.get(url);//LruCache 本质上就是map 可以直接获得
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private CommentListView mlistView;
private String url="http://api.kanzhihu.com/getpostanswers/20150925/archive";//知乎API [ answers ]
private SwipeRefreshLayout refreshLayout;
private ListAdapter listAdapter;
List mjsonBeen;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle("");
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
initView();
mlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
switch (position){
case 0:
startActivity(new Intent(MainActivity.this,UsingTest.class));
break;
}
}
});
new asyncTask().execute(url);
refreshLayout.setColorSchemeResources(R.color.swipeRefreshLayout,
R.color.swipeRefreshLayout,
R.color.swipeRefreshLayout,
R.color.swipeRefreshLayout);
refreshLayout.setProgressViewEndTarget(true, 100);
refreshLayout.setProgressBackgroundColor(R.color.bg);
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
Message msg=Message.obtain();
msg.what=1;
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
});
}
private void initView() {
mlistView= (CommentListView) findViewById(R.id.id_listView);
refreshLayout= (SwipeRefreshLayout) findViewById(R.id.refreshLayout);
}
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what==1){
Toast.makeText(MainActivity.this, "刷新马上就好,请稍等哒~", Toast.LENGTH_SHORT).show();
refreshLayout.setRefreshing(false);
}
}
};
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
/*
* 侧边栏点击事件
* */
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
/*
* 实现网络的异步访问
* */
class asyncTask extends AsyncTask>{
@Override
protected List doInBackground(String... params) {
return getJsonData(params[0]);
}
@Override
protected void onPostExecute(List jsonBeen) {
super.onPostExecute(jsonBeen);
listAdapter=new ListAdapter(MainActivity.this,jsonBeen);
mlistView.setAdapter(listAdapter);
}
}
/*
* 将Url对应的json格式数据转化为我们所封装的JsonBean的对象
* */
private List getJsonData(String url) {//通过url获取data
List jsonList=new ArrayList<>();
try {
String jsonString=readStream(new URL(url).openStream());//打开json的字符串接收
JSONObject jsonObject;
JsonBean jsonBean;
jsonObject=new JSONObject(jsonString);//将获取到的json数据传入jsonObject;
JSONArray jsonArray=jsonObject.getJSONArray("answers");//去除data
for (int i=0;i
还差许多xml文件,博主没有贴上去,大部分是一些自定的样式和侧边栏。本篇博客旨在让博友们看懂如何进行异步处理网络数据和Lrucache是如何操作的。
Demo是Android Studio的,能够直接跑起来,最重要的是读者能够自己写一遍我相信肯定能掌握,编程在于多动手。
地址: http://download.csdn.net/detail/u013000304/9520878