Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Json數據的解析+ListView圖文混排+緩存算法Lrucache 仿知乎

Android Json數據的解析+ListView圖文混排+緩存算法Lrucache 仿知乎

編輯:關於Android編程

前幾天心血來潮,打算根據看知乎的API自己做一個小知乎,定制的過程遇到ListView的優化問題及圖片未緩存重加載等等許多問題,解決了以後打算和博友分享一下。

接口數據:http://api.kanzhihu.com/getpostanswers/20150925/archive

\\

首先,Json數據太常用,相信每一位開發者Json的解析都是必備的。我們要准備以下知識:

JavaBean,枚舉你需要的元素,用來存儲數據。

異步加載網絡內容的必備途徑,多線程加載+AsyncTask兩種方式。

Json解析的常見方法,JsonObject和JsonArray;

ListView優化,使用Viewholder

下面上代碼,側邊欄這裡就不說了,相信大家都會。

layout_main.xml

 

<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">
    </com.ynu.commando.classrewrite.commentlistview></linearlayout>
</linearlayout></scrollview>
</android.support.v4.widget.swiperefreshlayout>
圖文混排ListView的item文件:

 

item_id_listview.xml

 

<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="熱門回答">
    </textview></textview></com.ynu.commando.classrewrite.roundimageview></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顆衛星才能覆蓋全球?">
    </textview></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滑動得更加順暢。">
    </textview></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">
    </textview></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 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的,能夠直接跑起來,最重要的是讀者能夠自己寫一遍我相信肯定能掌握,編程在於多動手。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved