AndroidカスタムViewおよびイベント
直接問題のように、本例の効果は下図のように、ImageViewから継承され、後ろに円が描かれ、円の大きさはレイアウトファイルに設定され、円内をクリックするとイベントが対応し、円外は応答しない.
まずカスタムMyViewコードを見てみましょう
radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0);
関連するコードはres/valusの下でattrsを新規に作成する.xmlとコンテンツの追加
すなわち、イベントが円内で発生するとカスタムメソッドがトリガーされ、円外の直接無視されます.
That's all!nice day,isn't it?
まずカスタムMyViewコードを見てみましょう
package test.bg;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.ImageView;
public class MyView extends ImageView implements OnGlobalLayoutListener
{
private int radius = 0;
private RectF rectF = null;
private test.bg.OnTouchListener mOnTouchListener=null;
public MyView(Context context)
{
super(context);
TypedArray ta = context.obtainStyledAttributes(R.styleable.MyView);
radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0);
rectF = new RectF(0, 0, 2 * radius, 2 * radius);
ta.recycle();
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalLayoutListener(this);
}
public MyView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.MyView);
radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0);
rectF = new RectF(0, 0, 2 * radius, 2 * radius);
ta.recycle();
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalLayoutListener(this);
}
public MyView(Context context, AttributeSet attrs)
{
super(context, attrs);
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.MyView);
radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0);
rectF = new RectF(0, 0, 2 * radius, 2 * radius);
ta.recycle();
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalLayoutListener(this);
}
public void setOnTouchListener(test.bg.OnTouchListener mOnTouchListener){
this.mOnTouchListener=mOnTouchListener;
}
@Override
protected void onDraw(Canvas canvas)
{
Paint p = new Paint();
p.reset();
p.setARGB(255, 255, 0, 0);
canvas.drawRoundRect(rectF, radius, radius, p);
super.onDraw(canvas);
}
@Override
public void onGlobalLayout()
{
LayoutParams lp = getLayoutParams();
lp.height = 2 * radius;
lp.width = 2 * radius;
setLayoutParams(lp);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
float x = event.getX();
float y = event.getY();
if ((x-radius)*(x-radius)+(y-radius)*(y-radius)<radius*radius)
{
Log.d("nei", ""+x+":"+y);
return mOnTouchListener.onTouchEvent(this, event);
}
else
{
Log.d("wai", ""+x+":"+y);
return false;
}
}
}
TypedArrayta=context.obtainStyledAttributes(R.styleable.MyView); radius = ta.getDimensionPixelSize(R.styleable.MyView_radius, 0);
関連するコードはres/valusの下でattrsを新規に作成する.xmlとコンテンツの追加
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyView">
<attr name="radius" format="dimension" />
</declare-styleable>
</resources>
では、プロパティとプロパティのタイプが定義され、レイアウトファイルでプロパティを設定するときにxmlns:gain="を追加する必要があります.http://schemas.android.com/apk/res/test.bg「gainはプロパティの接頭辞、test.bgはパッケージ名、完全なレイアウトファイルは次のとおりです.<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:gain="http://schemas.android.com/apk/res/test.bg"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<test.bg.MyView
android:id="@+id/mv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
gain:radius="160dp"
android:src="@drawable/ic_launcher" >
</test.bg.MyView>
</LinearLayout>
はつないで直接MyViewを使うことができて、コードは以下の通りですpackage test.bg;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
public class TestBgActivity extends Activity implements OnTouchListener
{
MyView mv;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
MyView mv = (MyView) findViewById(R.id.mv);
mv.setOnTouchListener(this);
}
@Override
public boolean onTouchEvent(View v, MotionEvent event)
{
if (v.getId() == R.id.mv)
{
Toast.makeText(this,
"haha:" + v.getId() + ":" + event.getX() + ":"
+ event.getY(),
Toast.LENGTH_SHORT).show();
}
return false;
}
}
ここのOnTouchListenerはシステムのOnTouchListenerではなく、私たち自身が定義したものであり、名前が同じにすぎないことに気づきました.定義は以下の通りです.package test.bg;
import android.view.MotionEvent;
import android.view.View;
public interface OnTouchListener
{
public boolean onTouchEvent(View v, MotionEvent event);
}
なぜ自分でOnTouchListenerを定義するのですか?Androidの従来のコントロールはすべて矩形領域でイベントに応答するため、カスタムのコントロールは自分の必要な形状に応じてイベントに応答することができ、例えば本例の円形イベント領域で応答することができ、円内ではなく、円の外接矩形内でも応答できない.私たちはMyViewでViewのonTouchEvent(MotionEvent event)方法を実現してフィルタリングを行うため、つまりコードである. @Override
public boolean onTouchEvent(MotionEvent event)
{
float x = event.getX();
float y = event.getY();
if ((x-radius)*(x-radius)+(y-radius)*(y-radius)<radius*radius)
{
Log.d("nei", ""+x+":"+y);
return mOnTouchListener.onTouchEvent(this, event);
}
else
{
Log.d("wai", ""+x+":"+y);
return false;
}
}
すなわち、イベントが円内で発生するとカスタムメソッドがトリガーされ、円外の直接無視されます.
That's all!nice day,isn't it?