目的:
顯示多行兩列的網格圖檔清單,圖檔資源來源于伺服器。
思路:
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){
}
});
}
}
}
接下來看看效果,依然很差,下一章用庫試試。