使用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(5000);
        // 设置请求类型为Get类型
        conn.setRequestMethod("GET");
        // 判断请求Url是否成功
        if (conn.getResponseCode() != 200) {
            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[10];
/*图片渲染在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 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:

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

                    pageviews[pos].setMaxHeight(640);
                    pageviews[pos].setMaxWidth(480);
                    pageviews[pos].setMinimumHeight(640);
                    pageviews[pos].setMinimumWidth(480);
                    break;
            }
        }
    };
public void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.mydefstyle);
        super.onCreate(savedInstanceState);
        curcontext=this;
        //设置当前的Activity的界面布局
        setContentView(R.layout.yujiahomepage);
        pageviews=new ImageView[10];
        bitmapfactory=new Bitmap[10];
        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 0;
    }

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[0]){
                Log.d(TAG,"homepiclist.data.weibo[" +position +"].picinfo[0] is null");
            }else if(null==homepiclist.data.weibo[position].picinfo[0].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[0].pic_url);
                            bitmapfactory[position] = BitmapFactory.decodeByteArray(data, 0, 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(640);
            pageviews[position].setMaxWidth(480);
            pageviews[position].setMinimumHeight(640);
            pageviews[position].setMinimumWidth(480);
            //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(5000);
        // 设置请求类型为Get类型
        conn.setRequestMethod("GET");
        // 判断请求Url是否成功
        if (conn.getResponseCode() != 200) {
            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(10, TimeUnit.SECONDS)//设置超时时间
                .readTimeout(10, TimeUnit.SECONDS)//设置读取超时时间
                .writeTimeout(10, 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 0;
        }catch (Exception e){
            e.printStackTrace();
        }
        return -1;
    }
    /**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 0;
        }catch (Exception e){
            e.printStackTrace();
        }
        return -1;
    }

接下来在主线程调用:

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

接下来看看效果,依然很差,下一章用库试试。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章