2012-08-21

Android Adapter家族系列(三) - BaseAdapter

今天介紹Adapter家族內一個本人還蠻常用到的子類別BaseAdapter

幾乎元件在使用自定義的方式呈現時最常出現的一個子類別

因為自定義的view是自由設定的,所以不能像ArrayAdapter只是單純

只帶入資料而已,必須對view內的元件也做設定及加入事件等

也就是說主要的Activity只是建構此子類別,帶入相關需要的參數

其他的自定義view元件想要怎麼加、想要怎麼設定、做什麼事

都是在BaseAdapter內做設定就可以了,這樣說應該聽了還是霧煞煞

我們用實際的程式碼來邊做邊說明好了。

這裡我們用自定義的ListView來做說明:

ListView listView01 = (ListView)findViewById(R.id.listView1); 
     CharSequence[] list = MemorandumDate;
     CharSequence[] list2 = MemorandumNote;
     CharSequence[] list3 = MemorandumPW;
     BaseAdapter adapter = new MyAdapter(this, list, mID, list2, list3, mreminder);  
     listView01.setAdapter(adapter);

說明:
在主要的Activity中,找到ListView元件,然後我們建構一個適配器adapter

CharSequence[]是字元陣列,用來儲存文字用的,我同時在其他地方有宣告三個字元陣列

(list、list2、list3),也分別給於其值(此部份請自己依所需撰寫)及兩個整數陣列(mID、mreminder)

帶入建構子中建構該元件後帶入ListView,要帶入什麼參數和參數的順序是依照規劃MyAdapter這個

BaseAdapter子類別時怎麼開的,這裡就會是需要什麼樣的參數和順序,下面貼出MyAdapter的程式碼。


package jim.memorandum;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MyAdapter extends BaseAdapter{  
 public LayoutInflater myInflater;
 public Context context;
    CharSequence[] mDate = null;
    CharSequence[] mData = null;
    CharSequence[] mPw = null;
    int[] MID = null;
    int[] mreminder = null;
    
    public MyAdapter(Context ctxt, CharSequence[] mdate,int[] mID,CharSequence[] mdata,CharSequence[] mpw,int[] mreminder){
        myInflater = LayoutInflater.from(ctxt);
        this.mDate = mdate;
        this.MID = mID;
        this.mData = mdata;
        this.context = ctxt;
        this.mPw = mpw;
        this.mreminder = mreminder;
    }

 public int getCount() {
  // TODO Auto-generated method stub
  return MID.length;
 }

 public Object getItem(int position) {
  // TODO Auto-generated method stub
  return MID[position];
 }

 public long getItemId(int position) {
  // TODO Auto-generated method stub
  return position;
 }

 public View getView(int position, View convertView, ViewGroup parent) {
  final int index = position;
         
  ViewTag viewTag;
        
        if(convertView == null){
            
            convertView = myInflater.inflate(R.layout.list_data, null);
            
           
            viewTag = new ViewTag(
                    (TextView)convertView.findViewById(R.id.tv_Date),
                    (TextView)convertView.findViewById(R.id.tv_Data),
                    (ImageButton)convertView.findViewById(R.id.ibtn_edit),
                    (ImageButton)convertView.findViewById(R.id.ibtn_del),
                    (ImageView)convertView.findViewById(R.id.img_remind),
                    (ImageView)convertView.findViewById(R.id.img_lock)
                    );
            
            convertView.setTag(viewTag);
        }
        else{
            viewTag = (ViewTag) convertView.getTag();
        }
        
        //如果有設定密碼就只顯示前五個字
        String stitle =""; 
        if(!mPw[position].toString().equalsIgnoreCase("")){
         stitle = mData[position].toString();
         if(stitle.length() > 3){
         stitle = stitle.subSequence(0, 3) + "...";
         }
         else
         {
          stitle = mData[position].toString();
         }
        }
        else
        {
         stitle = mData[position].toString();
        }
        
        viewTag.tvdata.setText(stitle);
        viewTag.tvdate.setText(mDate[position].toString());    
        
        //提醒
        if(mreminder[index] == 1)
        {
           viewTag.img_reminder.setImageResource(R.drawable.checkbox_on);
        }
        else
        {
          viewTag.img_reminder.setImageResource(R.drawable.checkbox_off);
        }
           
       
        //是否有密碼
        if(!mPw[index].toString().equalsIgnoreCase(""))
        {
           viewTag.img_lock.setImageResource(R.drawable.lock_on);
        }
        else
        {
          viewTag.img_lock.setImageResource(R.drawable.lock_off);
        }
        
        
        //修改按鈕
        final ImageButton btn_edit = (ImageButton) convertView.findViewById(R.id.ibtn_edit);
        btn_edit.setOnClickListener(new View.OnClickListener()  {
          public void onClick(View v)  {
         
         //有密碼時必須輸入密碼
         if(!mPw[index].toString().equalsIgnoreCase(""))
         {
          LayoutInflater factory=LayoutInflater.from(context);
           //得到自定義對話框
                final View DialogView=factory.inflate(R.layout.dialog, null);
                
                AlertDialog.Builder builder = new AlertDialog.Builder(context);
                builder.setTitle("請輸入密碼");
                builder.setCancelable(false);
                builder.setView(DialogView); //設置自定義對話框樣式
             builder.setPositiveButton("送出", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                       try
                         {
                            EditText pw = (EditText)DialogView.findViewById(R.id.pw_dialog);//輸入之密碼
                            //驗證密碼是否相同
                            String sPW1 = mPw[index].toString();//原密碼
                            String sPW2 = pw.getText().toString();//驗證密碼
                            if(sPW1.equals(sPW2))
                            {
                             //相同就進入 
                                Intent intent_request = new Intent(context, Edit.class);
                                String val=String.valueOf(MID[index]);
                                intent_request.putExtra("val", val); 
                                context.startActivity(intent_request);    
                              
                            }else
                            {
                             //不相同時顯示密碼錯誤
                                Toast.makeText(context,"密碼錯誤!", Toast.LENGTH_SHORT).show(); 
                            }                   
                          }catch(Exception e)
                          {
                             e.printStackTrace();
                          }
                  }
             });
             
             builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int id) {
              }
             });  
                
             AlertDialog alert = builder.create();
             alert.show();
             
         }else
         {
          Intent intent_request = new Intent(context, Edit.class);
             String val=String.valueOf(MID[index]);
             intent_request.putExtra("val", val); 
                context.startActivity(intent_request);
         }
          }
        });
        
        //刪除按鈕
        final ImageButton btn_del = (ImageButton) convertView.findViewById(R.id.ibtn_del);
        btn_del.setTag(position);
        btn_del.setOnClickListener(new View.OnClickListener()  {
         
          public void onClick(View v)  {
           AlertDialog.Builder builder = new AlertDialog.Builder(context);
           
           builder.setMessage("確定要刪除?");
           builder.setCancelable(false);
           
           builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int dialog_id) {
           SQLiteDatabase db;
           NewListDataSQL helper = new NewListDataSQL(context, "MemorandumSQL");
           db = helper.getReadableDatabase();
           long long1 = db.delete("newMemorandum", "_ID=" + MID[index], null);               
        //添加成功後返回行號,失敗後返回-1
      if (long1 == -1) {
       Toast.makeText(context,
         "刪除失敗!", Toast.LENGTH_SHORT)
         .show();
      } else {
       Intent intent = new Intent(context, MemorandumActivity.class);
                context.startActivity(intent);
                quit();//關閉,讓back鍵不會出現上一頁
      }
    
                }
            });
           builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
          public void onClick(DialogInterface dialog, int id) {
          }
         });   
         
         AlertDialog alert = builder.create();
         alert.show();
          }
        });
        
        return convertView;
 }
   
    public class ViewTag{
     TextView tvdate;
        TextView tvdata;
        ImageButton ibtnEdit;
        ImageButton ibtnDel;
        ImageView img_reminder;
        ImageView img_lock;
        
        public ViewTag(TextView date, TextView data, ImageButton edit, ImageButton del, ImageView img1, ImageView img2){
            this.tvdate = date;
            this.tvdata = data;
            this.ibtnEdit = edit;
            this.ibtnDel = del;
            this.img_reminder = img1;
            this.img_lock = img2;
            
        }
    }
    
    public void quit() {
        int pid = android.os.Process.myPid();
        android.os.Process.killProcess(pid);
        System.exit(0);
    }
    
}


說明:
1.MyAdapter是繼承自BaseAdapter這個子類別

2.LayoutInflater myInflater 指的是一種擴充,就是說如果你想用自定的XML配置檔

可以用此擴充把自定的XML置檔加入到現有元件中

3.Activity在建構此類別時,需要帶入什麼參數是在MyAdapter 內設定的
如果這個也不了解請麻煩去翻一下物件導向書籍

4.重點在getView內,所有的自定介面設定、處理都在getView內產生及設定、處理

5.有使用一個viewTag的元件設定類別,要告訴介面自定義的XML內有哪些元件配置


貼出自定義的XML配置檔,讓各位更了解崁在ListView內的是怎麼配置的

list_data.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:descendantFocusability="blocksDescendants"
    android:background="@drawable/button_style" >

    <RelativeLayout
        android:id="@+id/relativeLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >


        <TextView
            android:id="@+id/tv_Date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:textSize="20dp" />
        
        
        <ImageView
        android:id="@+id/img_lock"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/img_remind"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/password"
        />
        
        
        <ImageView
        android:id="@+id/img_remind"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/ibtn_edit"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/remind"
        />
        

        <ImageButton
            android:id="@+id/ibtn_del"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentRight="true"
            android:layout_alignParentTop="true"
            android:src="@android:drawable/ic_menu_delete"
            android:contentDescription="@string/del" />


        <ImageButton
            android:id="@+id/ibtn_edit"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_alignParentTop="true"
            android:layout_toLeftOf="@+id/ibtn_del"
            android:src="@android:drawable/ic_menu_edit"
            android:contentDescription="@string/edit" />

    </RelativeLayout>

    <TextView
        android:id="@+id/tv_Data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>


實際圖型:



後記:
這個是ListView中的自定義圖型,當然這是有美化過的,所以看起來才會有點不一樣

不過如果你想要做到像這個樣子,就一定要用BaseAdapter,然後套用自定義XML配置檔list_data.xml

不然是做不出這樣很自由的變化配置的,以上介紹,謝謝。

2 則留言:

  1. 我用BASEADAPTER去接收資料庫...
    可是有三筆資料卻都只顯示同一筆..
    if(cursor.moveToFirst()){
    for(int i = 0;i< cursor.getCount();i++){

    tag.id.setText(cursor.getString( 0));
    tag.content.setText(cursor.getString(1));
    cursor.moveToNext();

    }
    }

    回覆刪除
    回覆
    1. 看你的部份程式碼問題應該是在tag
      你用.setText應該只能寫一筆資料
      而你下一筆再用一次.setText只是把tag內的文字改掉而已
      所以你用迴圈跑還只有一筆資料會進tag而已
      我用的是陣列的方式,所以才會有多筆進去
      往這個方向檢查及修改,應該就沒問題了

      刪除

您的寶貴建議是我前進的動力!