有 2 层时,FrameLayout 无法正确处理触摸请求
FrameLayout doesn't handle touch request correct when having 2 layers
我正在尝试扩展 FrameLayout 以便能够决定为哪个视图传递触摸事件:
我的App是这样的:
点在水平滚动视图中,矩形只是一个视图。
当您触摸点区域时,它会滚动。我希望矩形可以拖动。
我一次可以做一个(我的意思是 - 如果框架只有一个 child)。但不是两者。
我想我需要重写:onInterceptTouchEvent 但没有设法将事件传递给矩形视图。这是我的代码:
activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.trashproject.FrameWithTouchControl
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:id="@+id/selector"
android:layout_width="45dp"
android:layout_height="match_parent"
android:background="#33FF0000" >
</View>
<HorizontalScrollView
android:id="@+id/horizontalScroll"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
<TableLayout
android:id="@+id/table"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal" >
</TableLayout>
</HorizontalScrollView>
</com.example.trashproject.FrameWithTouchControl>
</LinearLayout>
FrameWithTouchControl.java:
package com.example.trashproject;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
public class FrameWithTouchControl extends FrameLayout {
private static final String TAG ="FrameWithTouchControl" ;
private float curSelectorPositionX1;
private float curSelectorPositionX2;
private boolean isDragging = false;
private View mSelector;
private int mTouchSlop;
public FrameWithTouchControl(Context context) {
super(context);
init();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
initViewMembers();
}
public FrameWithTouchControl(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FrameWithTouchControl(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init();
}
private void initViewMembers() {
mSelector = this.findViewById(R.id.selector);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
// Always handle the case of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the scroll.
isDragging = false;
return false; // Do not intercept touch event, let the child handle it
}
curSelectorPositionX1 = mSelector.getLeft();
curSelectorPositionX2 = mSelector.getRight();
float evX = ev.getX();
//if the touch is out of the selector's area
if (evX >= curSelectorPositionX2 || evX <= curSelectorPositionX1) {
return false;
}
switch (action) {
case MotionEvent.ACTION_MOVE:
if (isDragging) {
// We're currently dragging, so yes, intercept the
// touch event!
mSelector.onTouchEvent(ev);
return true;
}
mSelector.onTouchEvent(ev);
return true;
}//switch
return false;
}//onIntercept
}
MainActivity.java:
package com.example.trashproject;
import java.util.Calendar;
import java.util.Random;
import android.content.ClipData;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnDragListener;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TableRow.LayoutParams;
import android.widget.TextView;
public class MainActivity extends FragmentActivity{
private static final int ROWS =8;
private static final int COLS = 100;
private static final String TAG = "MainActivity";
private TableLayout mTable;
private TextView[][] mCircles;
private boolean[][] mData;
private LayoutInflater mInflater;
private FrameLayout mFrame;
private View mSelector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mData = generateFakeGuestsTimes();
mInflater =(LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
makeTable();
mFrame = (FrameLayout) findViewById(R.id.frame);
mSelector = findViewById(R.id.selector);
mSelector.setOnTouchListener(new OnTouchListener() {
boolean isDragging;
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "selector touch triggered" );
Log.d(TAG, event.toString());
int action = event.getAction();
float deltaX = 0;
if (action==MotionEvent.ACTION_DOWN && !isDragging) {
isDragging = true;
deltaX = event.getX();
return true;
} else if (isDragging) {
if (action== MotionEvent.ACTION_MOVE) {
v.setX(v.getX() + event.getX() - deltaX);
} else if (action == MotionEvent.ACTION_CANCEL) {
isDragging = false;
return true;
} else if (action == MotionEvent.ACTION_UP) {
isDragging = false;
return false;
}
}
return false;
}
});
}
/**** NOT RELEVANT FROM HERE *******/
private boolean[][] generateFakeGuestsTimes() {
boolean[][] values = new boolean[ROWS][COLS];
Random rand = new Random();
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS ; j++) {
values[i][j] = rand.nextBoolean();
}
}
return values;
}
public void onClick(View view) {
Log.d(TAG, "numOfChildren" + mTable.getChildCount());
}
private void makeTable() {
mTable = (TableLayout) findViewById(R.id.table);
TableRow.LayoutParams rowParams = new TableRow.LayoutParams();
rowParams.width = LayoutParams.WRAP_CONTENT;
rowParams.height = 67;
mCircles = new TextView[ROWS][COLS];
final TableRow[] row = new TableRow[ROWS];
final TextView[] headerText = new TextView[ROWS];
long start = cal.getTimeInMillis();
for (int i = 0; i < ROWS; i++) {
row[i] = new TableRow(this);
row[i].setLayoutParams(rowParams);
for (int j = 0; j < COLS; j++) {
mCircles[i][j] = (TextView) mInflater.inflate(R.layout.calendar_month_grid, null);
if (mData[i][j]) {
mCircles[i][j].setBackgroundResource(R.drawable.small_circle);
} else {
mCircles[i][j].setBackgroundResource(R.drawable.small_circle_red);
}
row[i].addView(mCircles[i][j]);
}
mTable.addView(row[i]);
}//outer loop
long end = cal.getTimeInMillis();
Log.d(TAG, "time of operation=" + end + ", " + start + ", " + String.valueOf(end - start));
}
我通过在 FrameLayout 中切换顺序解决了这个问题。
显然,框架布局以相反的顺序设置图层。即:
<FrameLAyout>
<View1/>
<View2/>
</FrameLAyout>
View2会在上层。 View2 将首先获取触摸回调,如果未处理触摸,View1 将进行调用。
IE。这与绘图相同。底层是View1,上层是View2。有道理
我正在尝试扩展 FrameLayout 以便能够决定为哪个视图传递触摸事件:
我的App是这样的:
点在水平滚动视图中,矩形只是一个视图。 当您触摸点区域时,它会滚动。我希望矩形可以拖动。 我一次可以做一个(我的意思是 - 如果框架只有一个 child)。但不是两者。 我想我需要重写:onInterceptTouchEvent 但没有设法将事件传递给矩形视图。这是我的代码: activity_main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.example.trashproject.FrameWithTouchControl
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<View
android:id="@+id/selector"
android:layout_width="45dp"
android:layout_height="match_parent"
android:background="#33FF0000" >
</View>
<HorizontalScrollView
android:id="@+id/horizontalScroll"
android:layout_width="wrap_content"
android:layout_height="match_parent" >
<TableLayout
android:id="@+id/table"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal" >
</TableLayout>
</HorizontalScrollView>
</com.example.trashproject.FrameWithTouchControl>
</LinearLayout>
FrameWithTouchControl.java:
package com.example.trashproject;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
public class FrameWithTouchControl extends FrameLayout {
private static final String TAG ="FrameWithTouchControl" ;
private float curSelectorPositionX1;
private float curSelectorPositionX2;
private boolean isDragging = false;
private View mSelector;
private int mTouchSlop;
public FrameWithTouchControl(Context context) {
super(context);
init();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
initViewMembers();
}
public FrameWithTouchControl(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public FrameWithTouchControl(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
init();
}
private void initViewMembers() {
mSelector = this.findViewById(R.id.selector);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
// Always handle the case of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the scroll.
isDragging = false;
return false; // Do not intercept touch event, let the child handle it
}
curSelectorPositionX1 = mSelector.getLeft();
curSelectorPositionX2 = mSelector.getRight();
float evX = ev.getX();
//if the touch is out of the selector's area
if (evX >= curSelectorPositionX2 || evX <= curSelectorPositionX1) {
return false;
}
switch (action) {
case MotionEvent.ACTION_MOVE:
if (isDragging) {
// We're currently dragging, so yes, intercept the
// touch event!
mSelector.onTouchEvent(ev);
return true;
}
mSelector.onTouchEvent(ev);
return true;
}//switch
return false;
}//onIntercept
}
MainActivity.java:
package com.example.trashproject;
import java.util.Calendar;
import java.util.Random;
import android.content.ClipData;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnDragListener;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TableRow.LayoutParams;
import android.widget.TextView;
public class MainActivity extends FragmentActivity{
private static final int ROWS =8;
private static final int COLS = 100;
private static final String TAG = "MainActivity";
private TableLayout mTable;
private TextView[][] mCircles;
private boolean[][] mData;
private LayoutInflater mInflater;
private FrameLayout mFrame;
private View mSelector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mData = generateFakeGuestsTimes();
mInflater =(LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
makeTable();
mFrame = (FrameLayout) findViewById(R.id.frame);
mSelector = findViewById(R.id.selector);
mSelector.setOnTouchListener(new OnTouchListener() {
boolean isDragging;
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "selector touch triggered" );
Log.d(TAG, event.toString());
int action = event.getAction();
float deltaX = 0;
if (action==MotionEvent.ACTION_DOWN && !isDragging) {
isDragging = true;
deltaX = event.getX();
return true;
} else if (isDragging) {
if (action== MotionEvent.ACTION_MOVE) {
v.setX(v.getX() + event.getX() - deltaX);
} else if (action == MotionEvent.ACTION_CANCEL) {
isDragging = false;
return true;
} else if (action == MotionEvent.ACTION_UP) {
isDragging = false;
return false;
}
}
return false;
}
});
}
/**** NOT RELEVANT FROM HERE *******/
private boolean[][] generateFakeGuestsTimes() {
boolean[][] values = new boolean[ROWS][COLS];
Random rand = new Random();
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS ; j++) {
values[i][j] = rand.nextBoolean();
}
}
return values;
}
public void onClick(View view) {
Log.d(TAG, "numOfChildren" + mTable.getChildCount());
}
private void makeTable() {
mTable = (TableLayout) findViewById(R.id.table);
TableRow.LayoutParams rowParams = new TableRow.LayoutParams();
rowParams.width = LayoutParams.WRAP_CONTENT;
rowParams.height = 67;
mCircles = new TextView[ROWS][COLS];
final TableRow[] row = new TableRow[ROWS];
final TextView[] headerText = new TextView[ROWS];
long start = cal.getTimeInMillis();
for (int i = 0; i < ROWS; i++) {
row[i] = new TableRow(this);
row[i].setLayoutParams(rowParams);
for (int j = 0; j < COLS; j++) {
mCircles[i][j] = (TextView) mInflater.inflate(R.layout.calendar_month_grid, null);
if (mData[i][j]) {
mCircles[i][j].setBackgroundResource(R.drawable.small_circle);
} else {
mCircles[i][j].setBackgroundResource(R.drawable.small_circle_red);
}
row[i].addView(mCircles[i][j]);
}
mTable.addView(row[i]);
}//outer loop
long end = cal.getTimeInMillis();
Log.d(TAG, "time of operation=" + end + ", " + start + ", " + String.valueOf(end - start));
}
我通过在 FrameLayout 中切换顺序解决了这个问题。 显然,框架布局以相反的顺序设置图层。即:
<FrameLAyout>
<View1/>
<View2/>
</FrameLAyout>
View2会在上层。 View2 将首先获取触摸回调,如果未处理触摸,View1 将进行调用。 IE。这与绘图相同。底层是View1,上层是View2。有道理