天天看點

使用GridView顯示網絡圖檔<一>

目的:

顯示多行兩列的網格圖檔清單,圖檔資源來源于伺服器。

思路:

1. 布局元件兒,使用ABSListview的子類,常用的是有Listview GridView,因為要顯示多列,是以使用GridView。本例中用的是5行2列的GridView,一共十個圖檔,對應的是十個url。

2. 圖檔來源于伺服器,要從伺服器下載下傳圖檔,用到了Http協定,android java有一些比較優秀的http庫,這裡用的okhttp3,還有okhttputil。因我初學android,想摸索一下,暫時不用任何開源庫,想根據自己的思路來做,如果用到GridView顯示網絡圖檔,最直覺的想法就是用GridView和BaseAdapter搭配來顯示,在BaseAdapter的

public View getView(final int position, View convertView, ViewGroup parent) ;
           

在getView中從網絡下載下傳圖檔,然後渲染要傳回的圖檔,最後傳回這個ImageView。

下面粘貼部分代碼:

public static byte[] getImage(String path) throws Exception {

        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 設定連接配接逾時為5秒
        conn.setConnectTimeout();
        // 設定請求類型為Get類型
        conn.setRequestMethod("GET");
        // 判斷請求Url是否成功
        if (conn.getResponseCode() != ) {
            throw new RuntimeException("請求url失敗");
        }
        InputStream inStream = conn.getInputStream();
        byte[] bt = StreamTool.read(inStream);
        inStream.close();
        return bt;
    }
public class homepageview extends AppCompatActivity {
    static final private String TAG = "homepageview";
    private GridView GlobalPrevGridview = null;
    private Context curcontext;
    private final static String  HTML_JSON="http://101.200.213.137/weibov1/getHotWeibolist?T=NjllY2Y5OWU3NjRkNTA2ZDhjNjFiNzFlNmIxMGZjMDA&page=1&Q=VDJnNVJFNWxURkp4TUM5M2VIWlJiRUZUZGk5eE0yRktVMEU9";
    private hotweibolist homepiclist;//Json資料對應的結構體
    private Bitmap bitmapfactory[];
    private ImageView pageviews[];
        private int[] imageIds = new int[];
/*圖檔渲染在Handler中執行,因為如果直接在GridView的adapter的getView中請求URL并渲染傳回的圖檔,按照現在的做法,會導緻app界面上的圖檔多次刷,因為一個position對應一個url,對應一個圖檔,而在拖動GridView滑動清單的時候,這個position變化快,而網絡下載下傳圖檔可能會比較慢,也就會出現滑動一次清單,調用了多次下載下傳,下載下傳完成後就渲染,而這個時候渲染的内容未必跟目前的position是對應的,然後接着繼續下載下傳渲染,下載下傳渲染。》*/
    private Handler myuihandler=new Handler(){
        public  void handleMessage(Message msg){
             switch (msg.what){
                case :
                case :
                case :
                case :
                case :
                case :
                case :
                case :
                case :
                case :

                    int pos=msg.what;
                    if(null==pageviews[pos]){
                        pageviews[pos]=new ImageView(curcontext);
                    }
                    pageviews[pos].setImageBitmap(bitmapfactory[pos]);

                    pageviews[pos].setMaxHeight();
                    pageviews[pos].setMaxWidth();
                    pageviews[pos].setMinimumHeight();
                    pageviews[pos].setMinimumWidth();
                    break;
            }
        }
    };
public void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.mydefstyle);
        super.onCreate(savedInstanceState);
        curcontext=this;
        //設定目前的Activity的界面布局
        setContentView(R.layout.yujiahomepage);
        pageviews=new ImageView[];
        bitmapfactory=new Bitmap[];
        showimage=new ImageView(curcontext);
        assigned_ids();
        drawinit();
        }
private void assigned_ids() {
        homepageviewlistbottom = (GridView) findViewById(R.id.homepagebottomGview);
    }

    /*Gridview 初始化*/
  private int drawuserpicfromcloud() {
        GlobalPrevGridview=(GridView)findViewById(R.id.homepagecloudpics);

        GridViewAdapter gridViewAdapter = new GridViewAdapter();
        GlobalPrevGridview.setAdapter(gridViewAdapter);
        // 為GridView設定監聽器
        GlobalPrevGridview.setOnItemClickListener(new gridViewListener());

        return ;
    }

private class gridViewListener implements AdapterView.OnItemClickListener {

        @Override
        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                                long arg3) {
            // TODO Auto-generated method stub
            System.out.println("arg2 = " + arg2); // 列印出點選的位置
        }
    }
    private class GridViewAdapter extends BaseAdapter {
        class ViewHoldwe {
            private ImageView iv_url;
        };
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            if(null==pageviews[position]){
                pageviews[position]=new ImageView(curcontext);
            }
            Log.d(TAG,"getView position " + position);
            if(null==homepiclist){
                Log.d(TAG,"homepiclist is null");
            }else if(null==homepiclist.data){
                Log.d(TAG,"homepiclist.data is null");
            }else if(null==homepiclist.data.weibo[position]){
                Log.d(TAG,"homepiclist.data.weibo[" +position +"] is null");
            }else if(null==homepiclist.data.weibo[position].picinfo[]){
                Log.d(TAG,"homepiclist.data.weibo[" +position +"].picinfo[0] is null");
            }else if(null==homepiclist.data.weibo[position].picinfo[].pic_url){
                Log.d(TAG,"homepiclist.data.weibo[" +position +"].picinfo[0].pic_url is null");
            }else {
                new Thread() {
                    public void run() {
                        try {
                            byte[] data = com.example.xxxxx.yujiadenglu.socket_http.
                                    Hal_urlconnect.getImage(homepiclist.data.weibo[position].picinfo[].pic_url);
                            bitmapfactory[position] = BitmapFactory.decodeByteArray(data, , data.length);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        myuihandler.sendEmptyMessage(position);
                    }
                }.start();
            }          
            if (convertView == null)
            {
             }
            else
            {
                pageviews[position] = (ImageView)convertView;
            }
            //pageviews[position].setImageBitmap(bitmapfactory[position]);
            pageviews[position].setMaxHeight();
            pageviews[position].setMaxWidth();
            pageviews[position].setMinimumHeight();
            pageviews[position].setMinimumWidth();
            //imageView.setImageResource(imageIds[position]);
        //    pageviews[position].setImageDrawable(showimage.getDrawable());
            return pageviews[position];

        }

        /*
         * 功能:獲得目前選項的ID
         *
         * @see android.widget.Adapter#getItemId(int)
         */
        @Override
        public long getItemId(int position) {
            return position;
        }

        /*
         * 功能:獲得目前選項
         *
         * @see android.widget.Adapter#getItem(int)
         */
        @Override
        public Object getItem(int position) {
            return position;
        }

        /*
         * 獲得數量
         *
         * @see android.widget.Adapter#getCount()
         */
        @Override
        public int getCount() {
            return imageIds.length;
        }
    }
    private void drawinit() {
        drawuserpicfromcloud();
    }
    ......
}
           

運作效果:

圖檔位置跟postion不對應。

原因:

1. 能通路到圖檔資源,但是出現過通路失敗的情況。

2. http下載下傳圖檔的方式 需要優化

下面分析一下http部分:

其中com.example.xxxxx.yujiadenglu.socket_http.

Hal_urlconnect.getImage的代碼如下:

public class Hal_urlconnect {
     static hotweibolist retlist;
     public static byte[] getImage(String path) throws Exception {

        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 設定連接配接逾時為5秒
        conn.setConnectTimeout();
        // 設定請求類型為Get類型
        conn.setRequestMethod("GET");
        // 判斷請求Url是否成功
        if (conn.getResponseCode() != ) {
            throw new RuntimeException("請求url失敗");
        }
        InputStream inStream = conn.getInputStream();
        byte[] bt = StreamTool.read(inStream);
        inStream.close();
        return bt;
        }
           

沒有使用任何架構,改用如果okhttp3是否能好一些呢?下面使用Okhttp3的同步get方式

public class Hal_okhttp {

    private static final String TAG="Hal_okhttp";

 /*   okhttp3 同步 Get方法 */

    //同步get方式送出
    static byte[] okhttp_syncget(String urlstr) throws IOException{
        OkHttpClient mOkHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(urlstr)
                .build();
        Call call = mOkHttpClient.newCall(request);
        Response mResponse=call.execute();
        if (mResponse.isSuccessful()) {
            return mResponse.body().bytes();
        } else {
            throw new IOException("Unexpected code " + mResponse);
        }
    }
    }
           

沒有明顯改善,感覺通路網絡失敗的次數少了,但還是圖檔位置不對應。

下面改成okhttp3的異步get方式。

/* 以最新的okhttp3為例。
1.   Http-GET
1)        OkHttpClient:建立一個OkHttpClient執行個體,用于處理請求。
2)        Request:建構請求參數,如url,請求方式,請求參數,header等。
3)        Call:生成一個具體請求執行個體,相當于将請求封裝成了任務;兩種方式:
           ①、call.execute(),非異步方式,會阻塞線程,等待傳回結果。
           ②、call.enqueue(Callback),異步方式。
 onResponse回調的參數是response,
 一般情況下,比如我們希望獲得傳回的字元串,可以通過response.body().string()擷取;
 如果希望獲得傳回的二進制位元組數組,則調用response.body().bytes();
 如果你想拿到傳回的inputStream,則調用response.body().byteStream()。
 */
    public void okHttp_AsynGet(String urlstr)  {
        OkHttpClient  mOkHttpClient=new OkHttpClient();
        Request.Builder requestBuilder = new Request.Builder().url(urlstr);        //可以省略,預設是GET請求
        Request request = requestBuilder.build();
        Call mcall= mOkHttpClient.newCall(request);
        mcall.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG,"getAsynHttp onFailure");

            }            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if ( response.isSuccessful()) {
                    String str = response.body().string();
                    Log.i(TAG, "response.body().string---" + str);
                } else {
                    response.body().string();
                    String str = response.networkResponse().toString();
                    Log.i(TAG, "network---" + str);
                }
            }
        });
    }
           

上述是okhttp異步擷取資料的步驟。我們隻能從call.enqueue(Callback)中的Callback的onResponse中拿資料,根據這些資料做其他的操作。

如果每次調用一次okhttp3的異步get,都要寫new OkHttpClient();

newCall、Call中的回調函數,會顯得很繁瑣。

是以改為下列方式:

1. 一個單例okhttp

2. 在okhttp的資料回調函數中,把資料傳給調用線程,這需要在主線程傳給okhttp層函數一個函數指針,或者接口。

/*因為從網絡端擷取資源,圖檔可以轉成數組,json資料又可以轉成string,okhttps的異步Callback的onResponse的參數Response可以擷取bytes、string,但如果每次都寫一遍new Callback的話會很繁瑣*/
//首先聲明一個接口
public interface hal_okhttp3resp  {
         /**響應失敗*/
        void onReqFailed(String errorMsg);
        /*擷取傳回的字元串*/
        void onReqStrSuccess(String str);
         /*擷取傳回的byte[]*/
        void onReqBytearraySuccess(byte[] str);
}
/*單例*/

/**
     * 擷取單例引用
     */
         private static final String TAG="Hal_okhttp";
    private static volatile Hal_okhttp mInstance=null;//單例引用
    private OkHttpClient mOkHttpClient;//okHttpClient執行個體
    private android.os.Handler  okHttpHandler;//全局處理子線程和主線程通信
    public static Hal_okhttp getInstance(Context context) {
        if (mInstance == null) {
            synchronized (Hal_okhttp.class) {
                if (mInstance == null) {
                    mInstance = new Hal_okhttp(context);
                }
            }
        }
        return mInstance;
    }
    /**
     * 初始化OkHttpManager
     */
    private Hal_okhttp(Context context) {
        //初始化OkHttpClient
        mOkHttpClient = new OkHttpClient().newBuilder()
                .connectTimeout(, TimeUnit.SECONDS)//設定逾時時間
                .readTimeout(, TimeUnit.SECONDS)//設定讀取逾時時間
                .writeTimeout(, TimeUnit.SECONDS)//設定寫入逾時時間
                .build();
        //初始化Handler
        okHttpHandler = new Handler(context.getMainLooper());
    }
/*在okhttp的操作函數中,調用這幾個接口*/
    public int hal_okhttp3asyngetstring(String url, final hal_okhttp3resp calstr) {
        try {
            final Request request = new Request.Builder()
                    .url(url)
                    .build();
            Call call = mOkHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    Log.d(TAG,"通路失敗");
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (response.isSuccessful()) {
                        calstr.onReqStrSuccess(response.body().string());
                      } else {
                        Log.d(TAG,"伺服器錯誤");
                    }
                }
            });
            return ;
        }catch (Exception e){
            e.printStackTrace();
        }
        return -;
    }
    /**getAsynHttp
     * okHttp get異步請求
     * @param url 接口位址
     * @param calstr 請求傳回資料回調,參數類型是byte數組
     * @return
     */
public int hal_okhttp3asyngetbytes(String url, final hal_okhttp3resp calstr) {
        try {
            final Request request = new Request.Builder()
                    .url(url)
                    .build();
            Call call = mOkHttpClient.newCall(request);
            call.enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    Log.d(TAG,"通路失敗");
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (response.isSuccessful()) {
                        calstr.onReqBytearraySuccess(response.body().bytes());
                    } else {
                        Log.d(TAG,"伺服器錯誤");
                    }
                }
            });
            return ;
        }catch (Exception e){
            e.printStackTrace();
        }
        return -;
    }
           

接下來在主線程調用:

Hal_okhttp hero=Hal_okhttp.getInstance(curcontext);
     hero.hal_okhttp3asyngetbytes(homepiclist.data.weibo[position].picinfo[].pic_url, new hal_okhttp3resp<byte[]>() {
                            @Override
                            public void onReqSuccess(byte[] result) {
                                Log.d(TAG, "hal_okhttp3asynget onReqSuccess");
                                     bitmapfactory[position] = BitmapFactory.decodeByteArray(result, , result.length);
                                    pageviews[position].setImageBitmap(bitmapfactory[position]);
                            }

                            @Override
                            public void onReqFailed(String errorMsg) {
                                Log.d(TAG, "onReqFailed");
                            }
                            public  void onReqStrSuccess(String str){

                            }
                            /*擷取傳回的byte[]*/
                            public void onReqBytearraySuccess(byte[] result){
                                bitmapfactory[position] = BitmapFactory.decodeByteArray(result, , result.length);
                                myuihandler.sendEmptyMessage(position);
                                //pageviews[position].setImageBitmap(bitmapfactory[position]);
                            }
                        });
                        hero.hal_okhttp3asyngetstring(HTML_JSON,new hal_okhttp3resp<byte[]>(){
                            @Override
                            public void onReqSuccess(byte[] result) {
                                Log.d(TAG, "hal_okhttp3asynget onReqSuccess");
                             }

                            @Override
                            public void onReqFailed(String errorMsg) {
                                Log.d(TAG, "onReqFailed");
                            }
                            public  void onReqStrSuccess(String result){
                                Log.d(TAG,"form " + HTML_JSON +"  get : " + result);
                            }
                            /*擷取傳回的byte[]*/
                            public void onReqBytearraySuccess(byte[] result){
                             }
                        });
                    }
                }
                }           
           

接下來看看效果,依然很差,下一章用庫試試。