如何在片段内的对象中实现 onDragListener?
How do I implement onDragListener in an object within a fragment?
我的应用有一个 class、MarkedLine
,它扩展了 View
。此 class 的实例显示在片段中。我希望用户能够做以下三件事:
- 通过 "pinch" 和 "stretch" 手势放大线条
- 触摸直线的任意一点并获取其坐标
- 移动线
我有前两个工作,但无法弄清楚第三个(拖动)。
每个MarkedLine
由一行水平方框组成,其中一些方框是彩色的。用户可以通过拉伸放大,点击方框改变颜色;我还希望他们能够在屏幕上移动线条,因为当它放大时,它会离开屏幕的边缘。
基本片段布局(fragment_marked_line
)如下(我删除了不相关的位、填充、边距等):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<packagepath.models.ToolboxButton
android:id="@+id/toolbarNext"
android:layout_width="@dimen/toolbar_icon_size"
android:layout_height="@dimen/toolbar_icon_size"
android:src="@drawable/next_line"
res:layout_constraintTop_toTopOf="parent" />
<packagepath.models.MarkedLine
android:id="@+id/markedLine"
android:layout_width="0dp"
android:layout_height="wrap_content"
res:layout_constraintStart_toStartOf="parent"
res:layout_constraintEnd_toEndOf="parent"
res:layout_constraintTop_toBottomOf="@+id/toolbarNext" />
</android.support.constraint.ConstraintLayout>
(所以基本上它是一个按钮,下面有一条全角线。该按钮允许用户调出下一行)。
Fragment代码(MarkedLineFragment
)如下(n.b。LineSet基本上只是一个MarkedLines的数组,还有一些额外的变量,比如创建时间、线尺寸等):
public class MarkedLineFragment extends Fragment {
LineSet mLineSet
MarkedLine mMarkedLine;
ToolboxButton btn_next;
int mItemNumber, mMaxItems;
public MarkedLineFragment() {}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(
R.layout.fragment_marked_line, container, false);
// Get view objects
btn_next = rootView.findViewById(R.id.toolbarNext);
mMarkedLine = rootView.findViewById(R.id.markedLine);
// Initialise the button
initialise_button();
// If the LineSet has already been set,
// pass it through to the MarkedLine
if(mLineSet != null) {
mMarkedLine.setLineSet(mLineSet);
mMaxItems = mLineSet.getNum_items();
}
// Initialise at line 1
mItemNumber = 1;
mMarkedLine.setCurrentItem(mItemNumber);
// Draw the MarkedLine
drawLine();
return rootView;
}
// Initialise the button so that it moves to the next line on clicking
public void initialise_button() {
btn_next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mItemNumber == mMaxItems) return;
else mItemNumber += 1;
set_new_item_number();
}
});
}
private void set_new_item_number() {
mMarkedLine.setCurrentItem(mItemNumber);
}
public void drawChart() {
if(mMarkedLine != null) mMarkedLine.postInvalidate();
}
}
最后,MarkedLine
class(我省略了线条是如何绘制的细节,因为我认为它不相关,而且它很长 - 但我可以添加如果需要的话):
public class MarkedLine extends View {
private LineSet mLineSet;
private int currentItem;
private int numBoxes;
private float canvas_height, canvas_width;
private float box_size;
private float minX, maxX, minY, maxY;
// Scaling (pinch & zoom) variables
private float scaleFactor = 1.0f; // Current scale factor
private ScaleGestureDetector detectorScale;// Detector for gestures
private GestureDetector detectorTouch; // Detector for tap gestures
public MarkedLine(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
detectorScale = new ScaleGestureDetector(thisContext, new MarkedLine.ScaleListener());
detectorTouch = new GestureDetector(thisContext, new MarkedLine.TouchListener());
}
public void setCallback(OnBoxTouched callback) { mCallback = callback; }
public void setLineSet(LineSet lineSet) {
mLineSet = lineSet;
numBoxes = mLineSet.getNum_boxes();
invalidate();
}
public void setCurrentItem(int newItemNumber) {
currentItem = newItemNumber;
invalidate();
}
protected void onDraw(Canvas canvas) {
if (mLineSet == null) return;
// Set up canvas
canvas.save();
canvas.scale(scaleFactor, scaleFactor);
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
// draw_boxes reads how many boxes make up the MarkedLine,
// calculates what size they need to be to fit on the canvas,
// and then draws them
draw_boxes();
// fill_in_line adds in the appropriate colours to the
// boxes in the line
fill_in_line();
canvas.restore();
}
// GRID EVENT FUNCTIONS - respond to User touching screen
// onTouchEvent
// User has touched the screen - trigger listeners
@Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
invalidate();
return true;
}
// LISTENERS
/*
* Respond to user touching the screen
*/
private class TouchListener extends GestureDetector.SimpleOnGestureListener {
public boolean onSingleTapUp(MotionEvent event) {
// Determine where the screen was touched
float xTouch = event.getX();
float yTouch = event.getY();
// Check that the touch was within the line; return if not
if(!touch_in_line(xTouch, yTouch)) return false;
// Figure out which Box was tapped
int xCell = getTouchedBox(xTouch);
// Now the box which was tapped is coloured in
colour_box(xCell);
return true;
}
}
/*
* Determine scale factor for zoom mode
* This can be called in View and Edit Activities
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float MIN_ZOOM = 1f; // Minimum zoom level
float MAX_ZOOM = 5f; // Maximum zoom level
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
一切正常。用户可以拉伸线以制作方框 bigger/smaller,然后点击线上的任何方框为其着色。但是,当用户拖动时,我无法让方框在屏幕上移动他们的手指在上面。
我假设我需要向某些东西添加一个 onDragListener,但我不知道是什么。我尝试使用一个 DragListener class,类似于 ScaleListener 和 TouchListener classes,带有一个 onDrag 方法(我只有几条虚拟线,所以我可以附加一个断点)。然后我声明了 class (dragListener) 的一个实例。我尝试使用 this.onDragListener(dragListener)
将其附加到 MarkedLine 构造函数中,但它没有响应拖动。
然后我在 Fragment 中尝试了类似的操作,将其附加到 onCreateView 中的 mMarkedLine class,但是当我尝试拖动时它再次没有响应。
我已阅读 Android 文档,其中建议使用 onDragListener class,但我显然做错了什么。
如果它对其他人有帮助,我通过在 MarkedLine
class:
的 onTouchEvent
中添加拖动检查来解决此问题
@Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
// Check for drag gestures
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX() - previousTranslateX;
startY = event.getY() - previousTranslateY;
break;
case MotionEvent.ACTION_UP:
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
case MotionEvent.ACTION_MOVE:
translateX = event.getX() - startX;
translateY = event.getY() - startY;
break;
}
invalidate();
return true;
}
我的应用有一个 class、MarkedLine
,它扩展了 View
。此 class 的实例显示在片段中。我希望用户能够做以下三件事:
- 通过 "pinch" 和 "stretch" 手势放大线条
- 触摸直线的任意一点并获取其坐标
- 移动线
我有前两个工作,但无法弄清楚第三个(拖动)。
每个MarkedLine
由一行水平方框组成,其中一些方框是彩色的。用户可以通过拉伸放大,点击方框改变颜色;我还希望他们能够在屏幕上移动线条,因为当它放大时,它会离开屏幕的边缘。
基本片段布局(fragment_marked_line
)如下(我删除了不相关的位、填充、边距等):
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:res="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<packagepath.models.ToolboxButton
android:id="@+id/toolbarNext"
android:layout_width="@dimen/toolbar_icon_size"
android:layout_height="@dimen/toolbar_icon_size"
android:src="@drawable/next_line"
res:layout_constraintTop_toTopOf="parent" />
<packagepath.models.MarkedLine
android:id="@+id/markedLine"
android:layout_width="0dp"
android:layout_height="wrap_content"
res:layout_constraintStart_toStartOf="parent"
res:layout_constraintEnd_toEndOf="parent"
res:layout_constraintTop_toBottomOf="@+id/toolbarNext" />
</android.support.constraint.ConstraintLayout>
(所以基本上它是一个按钮,下面有一条全角线。该按钮允许用户调出下一行)。
Fragment代码(MarkedLineFragment
)如下(n.b。LineSet基本上只是一个MarkedLines的数组,还有一些额外的变量,比如创建时间、线尺寸等):
public class MarkedLineFragment extends Fragment {
LineSet mLineSet
MarkedLine mMarkedLine;
ToolboxButton btn_next;
int mItemNumber, mMaxItems;
public MarkedLineFragment() {}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View rootView = inflater.inflate(
R.layout.fragment_marked_line, container, false);
// Get view objects
btn_next = rootView.findViewById(R.id.toolbarNext);
mMarkedLine = rootView.findViewById(R.id.markedLine);
// Initialise the button
initialise_button();
// If the LineSet has already been set,
// pass it through to the MarkedLine
if(mLineSet != null) {
mMarkedLine.setLineSet(mLineSet);
mMaxItems = mLineSet.getNum_items();
}
// Initialise at line 1
mItemNumber = 1;
mMarkedLine.setCurrentItem(mItemNumber);
// Draw the MarkedLine
drawLine();
return rootView;
}
// Initialise the button so that it moves to the next line on clicking
public void initialise_button() {
btn_next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mItemNumber == mMaxItems) return;
else mItemNumber += 1;
set_new_item_number();
}
});
}
private void set_new_item_number() {
mMarkedLine.setCurrentItem(mItemNumber);
}
public void drawChart() {
if(mMarkedLine != null) mMarkedLine.postInvalidate();
}
}
最后,MarkedLine
class(我省略了线条是如何绘制的细节,因为我认为它不相关,而且它很长 - 但我可以添加如果需要的话):
public class MarkedLine extends View {
private LineSet mLineSet;
private int currentItem;
private int numBoxes;
private float canvas_height, canvas_width;
private float box_size;
private float minX, maxX, minY, maxY;
// Scaling (pinch & zoom) variables
private float scaleFactor = 1.0f; // Current scale factor
private ScaleGestureDetector detectorScale;// Detector for gestures
private GestureDetector detectorTouch; // Detector for tap gestures
public MarkedLine(Context thisContext, AttributeSet attrs) {
super(thisContext, attrs);
detectorScale = new ScaleGestureDetector(thisContext, new MarkedLine.ScaleListener());
detectorTouch = new GestureDetector(thisContext, new MarkedLine.TouchListener());
}
public void setCallback(OnBoxTouched callback) { mCallback = callback; }
public void setLineSet(LineSet lineSet) {
mLineSet = lineSet;
numBoxes = mLineSet.getNum_boxes();
invalidate();
}
public void setCurrentItem(int newItemNumber) {
currentItem = newItemNumber;
invalidate();
}
protected void onDraw(Canvas canvas) {
if (mLineSet == null) return;
// Set up canvas
canvas.save();
canvas.scale(scaleFactor, scaleFactor);
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
// draw_boxes reads how many boxes make up the MarkedLine,
// calculates what size they need to be to fit on the canvas,
// and then draws them
draw_boxes();
// fill_in_line adds in the appropriate colours to the
// boxes in the line
fill_in_line();
canvas.restore();
}
// GRID EVENT FUNCTIONS - respond to User touching screen
// onTouchEvent
// User has touched the screen - trigger listeners
@Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
invalidate();
return true;
}
// LISTENERS
/*
* Respond to user touching the screen
*/
private class TouchListener extends GestureDetector.SimpleOnGestureListener {
public boolean onSingleTapUp(MotionEvent event) {
// Determine where the screen was touched
float xTouch = event.getX();
float yTouch = event.getY();
// Check that the touch was within the line; return if not
if(!touch_in_line(xTouch, yTouch)) return false;
// Figure out which Box was tapped
int xCell = getTouchedBox(xTouch);
// Now the box which was tapped is coloured in
colour_box(xCell);
return true;
}
}
/*
* Determine scale factor for zoom mode
* This can be called in View and Edit Activities
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float MIN_ZOOM = 1f; // Minimum zoom level
float MAX_ZOOM = 5f; // Maximum zoom level
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
}
一切正常。用户可以拉伸线以制作方框 bigger/smaller,然后点击线上的任何方框为其着色。但是,当用户拖动时,我无法让方框在屏幕上移动他们的手指在上面。
我假设我需要向某些东西添加一个 onDragListener,但我不知道是什么。我尝试使用一个 DragListener class,类似于 ScaleListener 和 TouchListener classes,带有一个 onDrag 方法(我只有几条虚拟线,所以我可以附加一个断点)。然后我声明了 class (dragListener) 的一个实例。我尝试使用 this.onDragListener(dragListener)
将其附加到 MarkedLine 构造函数中,但它没有响应拖动。
然后我在 Fragment 中尝试了类似的操作,将其附加到 onCreateView 中的 mMarkedLine class,但是当我尝试拖动时它再次没有响应。
我已阅读 Android 文档,其中建议使用 onDragListener class,但我显然做错了什么。
如果它对其他人有帮助,我通过在 MarkedLine
class:
onTouchEvent
中添加拖动检查来解决此问题
@Override
public boolean onTouchEvent(MotionEvent event) {
detectorScale.onTouchEvent(event);
detectorTouch.onTouchEvent(event);
// Check for drag gestures
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX() - previousTranslateX;
startY = event.getY() - previousTranslateY;
break;
case MotionEvent.ACTION_UP:
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
case MotionEvent.ACTION_MOVE:
translateX = event.getX() - startX;
translateY = event.getY() - startY;
break;
}
invalidate();
return true;
}