Android Json数据的解析+ListView图文混排+缓存算法Lrucache 仿知乎

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013000304/article/details/51417666

前几天心血来潮,打算根据看知乎的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





阅读更多
换一批

没有更多推荐了,返回首页