标签:
前几天心血来潮,打算根据看知乎的API自己做一个小知乎,定制的过程遇到ListView的优化问题及图片未缓存重加载等等许多问题,解决了以后打算和博友分享一下。
接口数据:http://api.kanzhihu.com/getpostanswers/20150925/archive
首先,Json数据太常用,相信每一位开发者Json的解析都是必备的。我们要准备以下知识:
JavaBean,枚举你需要的元素,用来存储数据。
异步加载网络内容的必备途径,多线程加载+AsyncTask两种方式。
Json解析的常见方法,JsonObject和JsonArray;
ListView优化,使用Viewholder
下面上代码,侧边栏这里就不说了,相信大家都会。
layout_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.SwipeRefreshLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/refreshLayout" xmlns:android="http://schemas.android.com/apk/res/android"> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:background="#fff" xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="5dp" android:background="#CCCCCC"/> <com.ynu.commando.ClassRewrite.CommentListView android:layout_width="match_parent" android:layout_height="match_parent" android:dividerHeight="5dp" android:divider="#ccc" android:id="@+id/id_listView"/> </LinearLayout> </ScrollView> </android.support.v4.widget.SwipeRefreshLayout>图文混排ListView的item文件:
item_id_listview.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="12dp" android:layout_marginTop="15dp" android:orientation="horizontal"> <com.ynu.commando.ClassRewrite.RoundImageView android:layout_width="25dp" android:layout_height="25dp" android:src="@mipmap/smile_two" android:id="@+id/img"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginTop="4dp" android:textSize="13dp" android:textColor="#999999" android:text="来自"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:layout_marginLeft="4dp" android:textColor="#3374c4" android:textSize="13dp" android:id="@+id/name" android:text="热门回答"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="12dp"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/title" android:layout_marginTop="6dp" android:maxHeight="150dp" android:textStyle="bold" android:lineSpacingExtra="2dp" android:textSize="15dp" android:text="为什么北斗卫星导航系统需要35颗卫星才能覆盖全球?"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="12dp"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/body" android:maxEms="50" android:layout_marginTop="6dp" android:lineSpacingExtra="5dp" android:ellipsize="end" android:textSize="14dp" android:text="笔者个人看法,RecyclerView只是一个对ListView的升级版,这个升级的主要目的是为了让这个view的效率更高,并且使用更加方便。我们知道,ListView通过使用ViewHolder来提升性能。ViewHolder通过保存item中使用到的控件的引用来减少findViewById的调用,以此使ListView滑动得更加顺畅。"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:layout_marginRight="12dp" android:layout_marginBottom="10dp" android:gravity="right"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/time" android:textColor="#e13939" android:text="2016-4-25 00:54:00"/> </LinearLayout> </LinearLayout>
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<JsonBean> 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<JsonBean> 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<String,Bitmap> 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<String,Void,Bitmap>{ 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<String,Bitmap>(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<JsonBean> 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<String,Void,List<JsonBean>>{ @Override protected List<JsonBean> doInBackground(String... params) { return getJsonData(params[0]); } @Override protected void onPostExecute(List<JsonBean> jsonBeen) { super.onPostExecute(jsonBeen); listAdapter=new ListAdapter(MainActivity.this,jsonBeen); mlistView.setAdapter(listAdapter); } } /* * 将Url对应的json格式数据转化为我们所封装的JsonBean的对象 * */ private List<JsonBean> getJsonData(String url) {//通过url获取data List<JsonBean> 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<jsonArray.length();i++){//循环取出 jsonObject=jsonArray.getJSONObject(i); jsonBean=new JsonBean(); jsonBean.url=jsonObject.getString("avatar");//对应加入item jsonBean.title=jsonObject.getString("title"); jsonBean.body=jsonObject.getString("summary"); jsonBean.name=jsonObject.getString("authorname"); jsonBean.time=jsonObject.getString("time"); jsonList.add(jsonBean);//将输入传入list } } catch (MalformedURLException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return jsonList;//返回list } /* * 从InputStream解析url返回的数组 * */ private String readStream(InputStream is){//从inputStream中读取数据 InputStreamReader isr; String result=""; try { String line=""; isr=new InputStreamReader(is,"utf-8");//字节流转换为字符流 BufferedReader br=new BufferedReader(isr);//将字符流转换为buffered类型 while ((line=br.readLine())!=null){ result+=line; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return result; } @Override public void onBackPressed() { DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { super.onBackPressed(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. /* * 获取menu * */ 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(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { /* * 右上角选项事件 * */ return true; } return super.onOptionsItemSelected(item); } }还差许多xml文件,博主没有贴上去,大部分是一些自定的样式和侧边栏。本篇博客旨在让博友们看懂如何进行异步处理网络数据和Lrucache是如何操作的。
Demo是Android Studio的,能够直接跑起来,最重要的是读者能够自己写一遍我相信肯定能掌握,编程在于多动手。
地址: http://download.csdn.net/detail/u013000304/9520878
Android Json数据的解析+ListView图文混排+缓存算法Lrucache 仿知乎
标签:
原文地址:http://blog.csdn.net/u013000304/article/details/51417666