目的:
显示多行两列的网格图片列表,图片资源来源于服务器。
思路:
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){
}
});
}
}
}
接下来看看效果,依然很差,下一章用库试试。