CheckedTextView のカスタマイズ、左に、(1)

左側にチェックがあるリスト、CheckedTextView のカスタマイズで実装する方法、
長いので分けて投稿します。今日は、準備編

今回は、ライブラリ化しません。使用するプロジェクト内に
カスタマイズの CheckedTextView と、attr.xml を用意すれうば、半分の作業が終わりです。

(1)
attrs.xml
という名前のリソースXMLres/values/ に以下のように
チェックボックスの属性を定義する規則を書きます。
  declare-styleable に、作成するカスタマイズの CheckedTextView名称を指定する

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="LeftCheckedTextView">
      <attr name="checked"   format="boolean" />
      <attr name="checkMark" format="reference" />
      <attr name="drawablePadding" format="dimension" />
   </declare-styleable>
</resources>

この、attrs.xml により、
カスタマイズの LeftCheckedTextView をレイアウトXMLで記述する時に指定する。

  app:checked       false|true でチェックの初期状態

  app:checkMark      チェックマークのリファレンス
   OS標準の四角いチェックボックの場合、
      app:checkMark="?android:attr/listChoiceIndicatorMultiple"
   OS標準のラジオボタンの場合、
      app:checkMark="?android:attr/listChoiceIndicatorSingle"

  app:drawablePadding   チェックマークとテキストの間の隙間


以下が、カスタマイズの CheckedTextView
左側にチェックボックスを置く CheckedTextView です。

package uran.sample;
import uran.sample.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Checkable;
import android.widget.TextView;
/**
 * LeftCheckedTextView
 */

public class LeftCheckedTextView extends TextView implements Checkable{
   private int mDrawablePadding;
   private boolean mChecked;
   private int mCheckMarkResource;
   private Drawable mCheckMarkDrawable;
   private int mBasePaddingRight;
   private int mCheckMarkWidth;

   private static final int CHECKED_STATE_SET = {
      android.R.attr.state_checked
   };

   public LeftCheckedTextView(Context context){
      this(context,null);
   }
   public LeftCheckedTextView(Context context, AttributeSet attrs) {
      this(context, attrs, 0);
   }
   public LeftCheckedTextView(Context context,AttributeSet attrs,int defStyle){
      super(context,attrs,defStyle);
      TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.LeftCheckedTextView, defStyle, 0);
      mDrawablePadding = a.getDimensionPixelSize(R.styleable.LeftCheckedTextView_drawablePadding, 0);
      Drawable d = a.getDrawable(R.styleable.LeftCheckedTextView_checkMark);
      if (d != null){
         setCheckMarkDrawable(d);
      }
      boolean checked = a.getBoolean(R.styleable.LeftCheckedTextView_checked, false);
      setChecked(checked);
      a.recycle();
   }
   @Override
   public void toggle() {
      setChecked(!mChecked);
   }
   @Override
   public boolean isChecked(){
      return mChecked;
   }
   /**
    * Changes the checked state of this text view.
    * @param checked true to check the text, false to uncheck it
    */

   @Override
   public void setChecked(boolean checked){
      if (mChecked != checked){
         mChecked = checked;
         refreshDrawableState();
      }
   }
   /**
    * Set the checkmark to a given Drawable, identified by its resourece id. This will be drawn
    * @param resid The Drawable to use for the checkmark.
    */

   public void setCheckMarkDrawable(int resid){
      if (resid != 0 && resid==mCheckMarkResource){
         return;
      }
      mCheckMarkResource = resid;
      Drawable d = null;
      if (mCheckMarkResource != 0){
         d = getResources().getDrawable(mCheckMarkResource);
      }
      setCheckMarkDrawable(d);
   }
   /**
    * Set the checkmark to a given Drawable. This will be drawn when {@link #isChecked()} is true.
    * @param d The Drawable to use for the checkmark.
    */

   public void setCheckMarkDrawable(Drawable d){
      if (mCheckMarkDrawable != null){
         mCheckMarkDrawable.setCallback(null);
         unscheduleDrawable(mCheckMarkDrawable);
      }
      if (d != null){
         d.setCallback(this);
         d.setVisible(getVisibility() == VISIBLE, false);
         d.setState(CHECKED_STATE_SET);
         setMinHeight(d.getIntrinsicHeight());
         mCheckMarkWidth = d.getIntrinsicWidth();
         mBasePaddingRight = getPaddingLeft();
         super.setPadding(getPaddingLeft() + mCheckMarkWidth + mDrawablePadding
                         ,getPaddingTop(), getPaddingRight(), getPaddingBottom());
         d.setState(getDrawableState());
      }
      mCheckMarkDrawable = d;
      requestLayout();
   }
   @Override
   public void setPadding(int left, int top, int right, int bottom) {
      if (mCheckMarkDrawable != null){
         mBasePaddingRight = left;
         super.setPadding(getPaddingLeft() + mCheckMarkWidth + mDrawablePadding
                         ,getPaddingTop(), getPaddingRight(), getPaddingBottom());
      }else{
         super.setPadding(left, top, right, bottom);
      }
   }
   @Override
   protected void onDraw(Canvas canvas){
      super.onDraw(canvas);
      final Drawable checkMarkDrawable = mCheckMarkDrawable;
      if (checkMarkDrawable != null){
         final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
         final int height = checkMarkDrawable.getIntrinsicHeight();
         int y = 0;
         switch(verticalGravity){
            case Gravity.BOTTOM:
               y = getHeight() - height;
               break;
            case Gravity.CENTER_VERTICAL:
               y = (getHeight() - height) / 2;
               break;
         }
         checkMarkDrawable.setBounds(mBasePaddingRight
                                    ,y
                                    ,mBasePaddingRight + mCheckMarkWidth
                                    ,y + height
         );
         checkMarkDrawable.draw(canvas);
      }
   }

   @Override
   protected int
 onCreateDrawableState(int extraSpace){
      final int drawableState = super.onCreateDrawableState(extraSpace + 1);
      if (isChecked()){
         mergeDrawableStates(drawableState, CHECKED_STATE_SET);
      }
      return drawableState;
   }
   @Override
   protected void drawableStateChanged(){
      super.drawableStateChanged();
      if (mCheckMarkDrawable != null){
         int
 myDrawableState = getDrawableState();
         // Set the state of the Drawable
         mCheckMarkDrawable.setState(myDrawableState);
         invalidate();
      }
   }
   @Override
   public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event){
      boolean populated = super.dispatchPopulateAccessibilityEvent(event);
      if (!populated){
         event.setChecked(mChecked);
      }
      return populated;
   }
}

次回は、この使い方。。