ฉันต้องการควบคุมง่าย ๆ : คอนเทนเนอร์ที่มีมุมมองอยู่ข้างใน ถ้าฉันแตะที่ภาชนะและเลื่อนนิ้วฉันต้องการเลื่อนมุมมองเพื่อติดตามนิ้วของฉัน
ฉันควรใช้คอนเทนเนอร์ (เลย์เอาต์) ชนิดใด ทำอย่างไร
ฉันไม่จำเป็นต้องใช้พื้นผิว แต่เป็นโครงร่างที่เรียบง่าย
ฉันต้องการควบคุมง่าย ๆ : คอนเทนเนอร์ที่มีมุมมองอยู่ข้างใน ถ้าฉันแตะที่ภาชนะและเลื่อนนิ้วฉันต้องการเลื่อนมุมมองเพื่อติดตามนิ้วของฉัน
ฉันควรใช้คอนเทนเนอร์ (เลย์เอาต์) ชนิดใด ทำอย่างไร
ฉันไม่จำเป็นต้องใช้พื้นผิว แต่เป็นโครงร่างที่เรียบง่าย
คำตอบ:
บางสิ่งเช่นนี้
public class MyActivity extends Activity implements View.OnTouchListener {
TextView _view;
ViewGroup _root;
private int _xDelta;
private int _yDelta;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_root = (ViewGroup)findViewById(R.id.root);
_view = new TextView(this);
_view.setText("TextView!!!!!!!!");
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 50);
layoutParams.leftMargin = 50;
layoutParams.topMargin = 50;
layoutParams.bottomMargin = -250;
layoutParams.rightMargin = -250;
_view.setLayoutParams(layoutParams);
_view.setOnTouchListener(this);
_root.addView(_view);
}
public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
break;
}
_root.invalidate();
return true;
}}
ในmain.xml
เวลาเพียงRelativeLayout
ด้วย@+id/root
layoutPrarms.rightMargin = -250
และเช่นเดียวกันกับbottomMargin
!! คุณอธิบายได้ไหม อย่างไรก็ตามขอบคุณมาก !!
ฉันพบวิธีง่าย ๆ ในการทำเช่นนั้นกับ ViewPropertyAnimator:
float dX, dY;
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
view.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
break;
default:
return false;
}
return true;
}
setX
และsetY
โดยตรงแทนการใช้ภาพเคลื่อนไหวของระยะเวลา 0.
ทำตามวิธีการ @Andrey หากคุณต้องการย้ายมุมมองจากจุดศูนย์กลางคุณจะต้องย่อส่วนความสูงและความกว้างครึ่งหนึ่งของมุมมองไปที่การเคลื่อนไหว
float dX, dY;
@Override
public boolean onTouchEvent(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
view.animate()
.x(event.getRawX() + dX - (view.getWidth() / 2))
.y(event.getRawY() + dY - (view.getHeight() / 2))
.setDuration(0)
.start();
break;
default:
return false;
}
return true;
}
การใช้งานเหมือนกันใน Kotlin
rightPanel.setOnTouchListener(View.OnTouchListener { view, event ->
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
rightDX = view!!.x - event.rawX
// rightDY = view!!.getY() - event.rawY;
}
MotionEvent.ACTION_MOVE -> {
var displacement = event.rawX + rightDX
view!!.animate()
.x(displacement)
// .y(event.getRawY() + rightDY)
.setDuration(0)
.start()
}
else -> { // Note the block
return@OnTouchListener false
}
}
true
})
แตะที่ภาชนะและมุมมองจะเป็นไปตามนิ้วของคุณ
รหัส xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/floating_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<ImageView
android:id="@+id/btn_chat"
android:layout_width="42dp"
android:layout_height="42dp"
/>
<LinearLayout>
รหัส Java
public class DashBoardActivity extends Activity implements View.OnClickListener, View.OnTouchListener {
float dX;
float dY;
int lastAction;
LinearLayout floatingLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
floatingLayout = findViewById(R.id.floating_layout);
floatingLayout.setOnTouchListener(this);
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
dX = view.getX() - event.getRawX();
dY = view.getY() - event.getRawY();
lastAction = MotionEvent.ACTION_DOWN;
break;
case MotionEvent.ACTION_MOVE:
view.setY(event.getRawY() + dY);
view.setX(event.getRawX() + dX);
lastAction = MotionEvent.ACTION_MOVE;
break;
case MotionEvent.ACTION_UP:
if (lastAction == MotionEvent.ACTION_DOWN)
Toast.makeText(DashBoardActivity.this, "Clicked!", Toast.LENGTH_SHORT).show();
break;
default:
return false;
}
return true;
}
}
ฉันแนะนำให้ใช้ view.translationX และ view.translationY เพื่อเลื่อนมุมมองของคุณ
ตัวอย่าง Kotlin:
yourView.translationX = xTouchCoordinate
yourView.translationY = yTouchCoordinate
สร้างคลาสฟังสัมผัสแบบกำหนดเอง (ใน Kotlin):
(รหัสนี้ จำกัด มุมมองของคุณไม่ให้ลากออกจากมุมมองพาเรนต์)
class CustomTouchListener(
val screenWidth: Int,
val screenHeight: Int
) : View.OnTouchListener {
private var dX: Float = 0f
private var dY: Float = 0f
override fun onTouch(view: View, event: MotionEvent): Boolean {
val newX: Float
val newY: Float
when (event.action) {
MotionEvent.ACTION_DOWN -> {
dX = view.x - event.rawX
dY = view.y - event.rawY
}
MotionEvent.ACTION_MOVE -> {
newX = event.rawX + dX
newY = event.rawY + dY
if ((newX <= 0 || newX >= screenWidth - view.width) || (newY <= 0 || newY >= screenHeight - view.height)) {
return true
}
view.animate()
.x(newX)
.y(newY)
.setDuration(0)
.start()
}
}
return true
}
}
วิธีใช้งาน
parentView.viewTreeObserver.addOnGlobalLayoutListener { view.setOnTouchListener(CustomTouchListener(parentView.width, parentView.height)) }
parentView
เป็นแม่ของมุมมองของคุณ
ในรหัสด้านล่างนี้ฉันได้สร้างสิ่งที่เรียกว่าRegionView
( git ) ซึ่งเป็นคอนเทนเนอร์ที่นำกลับมาใช้ใหม่ได้ซึ่งรับผิดชอบในการจัดการการลากและซูมสำหรับเด็กที่ซ้อนอยู่แต่ละตัว
ที่นี่เราจัดการtop
และleft
ค่าสัมประสิทธิ์ของเด็กView
's LayoutParams
กับการเคลื่อนไหวเกี่ยวกับการจำลองแผนภาพ โดย decoupling View
การตีความหมายของการจัดการสิ่งที่เข้าใจว่าการดำเนินการลากและสิ่งที่มุ่งมั่นที่จะดำเนินการระดับที่เราสามารถให้การจัดการความน่าเชื่อถือของเด็ก
package com.zonal.regionview;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Vibrator;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.RelativeLayout;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Alexander Thomas (@Cawfree) on 20/07/2017.
*/
/** Enables users to customize Regions Of Interest on a Canvas. */
public class RegionView extends RelativeLayout implements View.OnTouchListener, GestureDetector.OnGestureListener, ScaleGestureDetector.OnScaleGestureListener {
/* Member Variables. */
private final GestureDetector mGestureDetector;
private final ScaleGestureDetector mScaleGestureDetector;
private final Map<Integer, View> mViewMap;
private boolean mScaling;
private float mScale;
private boolean mWrapContent;
private boolean mDropOnScale;
public RegionView(Context context) {
// Implement the Parent.
super(context);
// Initialize Member Variables.
this.mGestureDetector = new GestureDetector(context, this);
this.mViewMap = new HashMap<>();
this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
this.mScaling = false;
this.mScale = Float.NaN;
this.mWrapContent = false;
this.mDropOnScale = false;
// Register ourself as the OnTouchListener.
this.setOnTouchListener(this);
}
public RegionView(Context context, @Nullable AttributeSet attrs) {
// Implement the Parent.
super(context, attrs);
// Initialize Member Variables.
this.mGestureDetector = new GestureDetector(context, this);
this.mViewMap = new HashMap<>();
this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
this.mScaling = false;
this.mWrapContent = false;
this.mDropOnScale = false;
// Register ourself as the OnTouchListener.
this.setOnTouchListener(this);
}
public RegionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
// Implement the Parent.
super(context, attrs, defStyleAttr);
// Initialize Member Variables.
this.mGestureDetector = new GestureDetector(context, this);
this.mViewMap = new HashMap<>();
this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
this.mScaling = false;
this.mWrapContent = false;
this.mDropOnScale = false;
// Register ourself as the OnTouchListener.
this.setOnTouchListener(this);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public RegionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
// Implement the Parent.
super(context, attrs, defStyleAttr, defStyleRes);
// Initialize Member Variables.
this.mGestureDetector = new GestureDetector(context, this);
this.mViewMap = new HashMap<>();
this.mScaleGestureDetector = new ScaleGestureDetector(context, this);
this.mScaling = false;
this.mWrapContent = false;
this.mDropOnScale = false;
// Register ourself as the OnTouchListener.
this.setOnTouchListener(this);
}
@Override
public boolean onTouch(final View v, final MotionEvent event) {
// Calculate the PointerId.
final int lPointerId = event.getPointerId(event.getActionIndex());
// Handle the TouchEvent.
this.getGestureDetector().onTouchEvent(event);
this.getScaleGestureDetector().onTouchEvent(event);
// Did the user release a pointer?
if(event.getAction() == MotionEvent.ACTION_UP) {
// Was there a View associated with this Action?
final View lView = this.getViewMap().get(lPointerId);
// Does the View exist?
if(lView != null) {
// Remove the View from the Map.
this.getViewMap().remove(lPointerId); /** TODO: Provide a Callback? */
}
}
// Consume all events for now.
return true;
}
@Override
public boolean onDown(MotionEvent e) {
// Calculate the PointerId.
final Integer lPointerId = Integer.valueOf(e.getPointerId(e.getActionIndex()));
// Fetch the View.
final View lView = this.getViewFor(Math.round(e.getRawX()), Math.round(e.getRawY()));
// Is it valid?
if(lView != null) {
// Watch the View.
this.getViewMap().put(lPointerId, lView);
// Configure the Anchor.
lView.setPivotX(0);
lView.setPivotY(0);
// Assert that we handled the event.
return true;
}
// Assert that we ignored the event.
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// Are we not scaling?
if(!this.isScaling()) {
// Calculate the PointerId.
final Integer lPointerId = Integer.valueOf(e1.getPointerId(e1.getActionIndex()));
// Fetch the View.
final View lView = this.getViewMap().get(lPointerId);
// Is the scroll valid for a given View?
if(lView != null) {
// Calculate the Scaled Width and Height of the View.
final float lWidth = lView.getWidth() * lView.getScaleX();
final float lHeight = lView.getHeight() * lView.getScaleY();
// Declare the initial position.
final int[] lPosition = new int[] { (int)(e2.getX() - ((lWidth) / 2)), (int)(e2.getY() - ((lHeight) / 2)) };
// Are we wrapping content?
if(this.isWrapContent()) {
// Wrap the Position.
this.onWrapContent(lPosition, lWidth, lHeight);
}
// Update the Drag.
this.onUpdateDrag(lView, lPosition);
}
// Assert we handled the scroll.
return true;
}
// Otherwise, don't permit scrolling. Don't consume the MotionEvent.
return false;
}
/** Forces X/Y values to be coerced within the confines of the RegionView. */
private final void onWrapContent(final int[] pPosition, final float pWidth, final float pHeight) {
// Limit the parameters. (Top-Left)
pPosition[0] = Math.max(pPosition[0], 0);
pPosition[1] = Math.max(pPosition[1], 0);
// Limit the parameters. (Bottom-Right)
pPosition[0] = Math.min(pPosition[0], (int)(this.getWidth() - pWidth));
pPosition[1] = Math.min(pPosition[1], (int)(this.getHeight() - pHeight));
}
/** Updates the Drag Position of a child View within the Layout. Implicitly, we update the LayoutParams of the View. */
private final void onUpdateDrag(final View pView, final int pLeft, final int pTop) {
// Allocate some new MarginLayoutParams.
final MarginLayoutParams lMarginLayoutParams = new MarginLayoutParams(pView.getLayoutParams());
// Update the Margin.
lMarginLayoutParams.setMargins(pLeft, pTop, 0, 0);
// Refactor the MarginLayoutParams into equivalent LayoutParams for the RelativeLayout.
pView.setLayoutParams(new RelativeLayout.LayoutParams(lMarginLayoutParams));
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
// Calculate the ScaleFactor.
float lScaleFactor = detector.getScaleFactor() - 1;
// Fetch the Scaled View.
final View lView = this.getViewMap().entrySet().iterator().next().getValue();
// Update the ScaleFactor.
final float lScale = this.getScale() + lScaleFactor;
// Calculate the Proposed Width and Height.
final int lWidth = Math.round(lView.getWidth() * lScale);
final int lHeight = Math.round(lView.getHeight() * lScale);
// Is the View already too large for wrap content?
if(lWidth >= this.getWidth() || lHeight >= this.getHeight()) {
// Don't update the scale.
return false;
}
// Persist this Scale for the View.
lView.setScaleX(lScale);
lView.setScaleY(lScale);
// Assign the Scale.
this.setScale(lScale);
// Compute the Position.
final int[] lPosition = new int[] { Math.round(detector.getFocusX()) - (lWidth / 2), Math.round(detector.getFocusY()) - (lHeight / 2) };
// Are we wrapping the Position?
if(this.isWrapContent()) {
// Wrap the Position.
this.onWrapContent(lPosition, lWidth, lHeight);
}
// Update the Drag.
this.onUpdateDrag(lView, lPosition);
// Assert that we handled the scale.
return true;
}
/** Update the Drag. */
private final void onUpdateDrag(final View pView, final int[] pPosition) {
// Call the sub-implementation.
this.onUpdateDrag(pView, pPosition[0], pPosition[1]);
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// Is the user not dragging at all?
if(this.getViewMap().size() == 1) {
// Fetch the View.
final View lView = this.getViewMap().entrySet().iterator().next().getValue();
// Initialize the Scale.
this.setScale(lView.getScaleX());
// Assert that we've started scaling.
this.setScaling(true);
// Inform the callback.
return true;
}
// Otherwise, don't allow scaling.
return false;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// Were we scaling?
if(this.isScaling()) {
// Assert that we've stopped scaling.
this.setScaling(false);
// Reset the Scale.
this.setScale(Float.NaN);
// Should we stop dragging now that we've finished scaling?
if(this.isDropOnScale()) {
// Clear the ViewMap.
this.getViewMap().clear();
}
}
}
/** Returns the View colliding with the given co-ordinates. */
private final View getViewFor(final int pX, final int pY) {
// Declare the LocationBuffer.
final int[] lLocationBuffer = new int[2];
// Iterate the Views.
for(int i = 0; i < this.getChildCount(); i++) {
// Fetch the child View.
final View lView = this.getChildAt(i);
// Fetch its absolute position.
lView.getLocationOnScreen(lLocationBuffer);
// Determine if the MotionEvent collides with the View.
if(pX > lLocationBuffer[0] && pY > lLocationBuffer[1] && (pX < lLocationBuffer[0] + (lView.getWidth() * lView.getScaleX())) && (pY < lLocationBuffer[1] + (lView.getHeight() * lView.getScaleY()))) {
// Return the View.
return lView;
}
}
// We couldn't find a View.
return null;
}
/* Unused Overrides. */
@Override public void onShowPress(MotionEvent e) { }
@Override public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override public void onLongPress(MotionEvent e) { }
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return false; }
/* Getters and Setters. */
private final GestureDetector getGestureDetector() {
return this.mGestureDetector;
}
private final ScaleGestureDetector getScaleGestureDetector() {
return this.mScaleGestureDetector;
}
private final Map<Integer, View> getViewMap() {
return this.mViewMap;
}
private final void setScaling(final boolean pIsScaling) {
this.mScaling = pIsScaling;
}
private final boolean isScaling() {
return this.mScaling;
}
private final void setScale(final float pScale) {
this.mScale = pScale;
}
private final float getScale() {
return this.mScale;
}
/** Defines whether we coerce the drag and zoom of child Views within the confines of the Layout. */
public final void setWrapContent(final boolean pIsWrapContent) {
this.mWrapContent = pIsWrapContent;
}
public final boolean isWrapContent() {
return this.mWrapContent;
}
/** Defines whether a drag operation is considered 'finished' once the user finishes scaling a view. */
public final void setDropOnScale(final boolean pIsDropOnScale) {
this.mDropOnScale = pIsDropOnScale;
}
public final boolean isDropOnScale() {
return this.mDropOnScale;
}
}
นี่ฉันแสดงกรณีตัวอย่างการใช้งาน:
package com.zonal.regionview;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.AnalogClock;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Allocate a RegionView.
final RegionView lRegionView = new RegionView(this);
// Add some example items to drag.
lRegionView.addView(new AnalogClock(this));
lRegionView.addView(new AnalogClock(this));
lRegionView.addView(new AnalogClock(this));
// Assert that we only want to drag Views within the confines of the RegionView.
lRegionView.setWrapContent(true);
// Assert that after we've finished scaling a View, we want to stop being able to drag it until a new drag is started.
lRegionView.setDropOnScale(true);
// Look at the RegionView.
this.setContentView(lRegionView);
}
}
เปลี่ยนวิธีการแก้ปัญหาโดย @Vyacheslav Shylkin เพื่อลบการอ้างอิงของหมายเลขที่ป้อนด้วยตนเอง
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.RelativeLayout;
public class MainActivity extends Activity implements View.OnTouchListener
{
private int _xDelta;
private int _yDelta;
private int _rightMargin;
private int _bottomMargin;
private ImageView _floatingView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this._floatingView = (ImageView) findViewById(R.id.textView);
this._floatingView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener()
{
@Override
public boolean onPreDraw()
{
if (_floatingView.getViewTreeObserver().isAlive())
_floatingView.getViewTreeObserver().removeOnPreDrawListener(this);
updateLayoutParams(_floatingView);
return false;
}
});
this._floatingView.setOnTouchListener(this);
}
private void updateLayoutParams(View view)
{
this._rightMargin = -view.getMeasuredWidth();
this._bottomMargin = -view.getMeasuredHeight();
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(view.getMeasuredWidth(), view.getMeasuredHeight());
layoutParams.bottomMargin = this._bottomMargin;
layoutParams.rightMargin = this._rightMargin;
view.setLayoutParams(layoutParams);
}
@Override
public boolean onTouch(View view, MotionEvent event)
{
if (view == this._floatingView)
{
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
this._xDelta = X - lParams.leftMargin;
this._yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = X - this._xDelta;
layoutParams.topMargin = Y - this._yDelta;
layoutParams.rightMargin = this._rightMargin;
layoutParams.bottomMargin = this._bottomMargin;
view.setLayoutParams(layoutParams);
break;
}
return true;
}
else
{
return false;
}
}
}
ในตัวอย่างนี้คุณสามารถย้ายมุมมองที่อยู่ภายในขอบเขตของพ่อแม่ไม่ว่าขนาดของมัน, ภาพเคลื่อนไหวที่สมบูรณ์แบบและจับคลิก
เหตุผลที่โซลูชันนี้เหนือกว่าความคิดเห็นอื่น ๆ คือวิธีนี้ใช้Directional Padซึ่งคำนวณตัวเองและจะไม่ส่งต่อตำแหน่งมุมมองซึ่งเป็นแหล่งที่มาของข้อบกพร่องจำนวนมาก
View view;
Animator.AnimatorListener listener;
boolean onMove = false;
boolean firstAnimation = true;
static final int CLICK_DURATION = 175;
float parentWidth;
float parentHeight;
// Those are the max bounds
// within the contianer
float xBoundMax;
float yBoundMax;
// This variables hold the target
// ordinates for the next
// animation in case an animation
// is already in progress.
float targetX;
float targetY;
float downRawX;
float downRawY;
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_UP:
if(event.getEventTime() - event.getDownTime() < CLICK_DURATION) click();
onMove = false;
break;
case MotionEvent.ACTION_DOWN:
firstAnimation = true;
xBoundMax = parentWidth - view.getWidth();
yBoundMax = parentHeight - view.getHeight();
downRawX = event.getRawX();
downRawY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if(!onMove)
{
if(event.getEventTime() - event.getDownTime() < CLICK_DURATION) break;
else onMove = true;
}
// Calculating the position the
// view should be posed at.
float offsetX = event.getRawX() - downRawX;
float offsetY = event.getRawY() - downRawY;
downRawX = event.getRawX();
downRawY = event.getRawY();
targetX = currentX + offsetX;
targetY = currentY.getY() + offsetY;
// Checking if view
// is within parent bounds
if(targetX > parentWidth - view.getWidth()) targetX = xBoundMax;
else if (targetX < 0) targetX = 0;
if(targetY > parentHeight - view.getHeight())targetY = yBoundMax;
else if (targetY < 0) targetY = 0;
// This check is becuase the user may just click on the view
// So if it's a not a click, animate slowly but fastly
// to the desired position
if(firstAnimation)
{
firstAnimation = false;
animate(70, getNewAnimationListener());
break;
}
if(listener != null) break;
animate(0, null);
break;
case MotionEvent.ACTION_BUTTON_PRESS:
default:
return false;
}
return true;
}
// Gets a new animation listener and reset
private android.animation.Animator.AnimatorListener getNewAnimationListener()
{
listener = new Animator.AnimatorListener() {
@Override public void onAnimationStart(Animator animation) { }
@Override public void onAnimationCancel(Animator animation) { }
@Override public void onAnimationRepeat(Animator animation) { }
@Override public void onAnimationEnd(Animator animation) {
animation.removeListener(listener);
listener = null;
view.setAnimation(null);
animate(0, null);
}
};
return listener;
}
private void animate(int duration, @Nullable Animator.AnimatorListener listener)
{
view.animate()
.x(targetX)
.y(targetY)
.setDuration(duration)
.setListener(listener)
.start();
currentX = targetX;
currentY = targetY;
}
private void click()
{
// Dohere stuff that you desire and than
}
หากคุณมีคอนเทนเนอร์ภายใน ScrollView หรือ ScrollView สองมิติคุณควรเพิ่มบรรทัดนี้ไปที่ onTouch
view.getParent().requestDisallowInterceptTouchEvent(true);
เหมือนกับคำตอบของ@ Alex Karshinฉันเปลี่ยนนิดหน่อย
public class MovingObject implements OnTouchListener {
private RelativeLayout.LayoutParams lParams;
private PointF viewPoint, prePoint, currPoint;
public MovingObject() {
lParams = null;
viewPoint = new PointF();
prePoint = new PointF();
currPoint = new PointF();
}
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
viewPoint.set(view.getX(), view.getY());
prePoint.set(event.getRawX(), event.getRawY());
lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
currPoint.set(event.getRawX(), event.getRawY());
moveToCurrentPoint(view);
break;
}
view.invalidate();
return true;
}
private void moveToCurrentPoint(View view) {
float dx = currPoint.x - prePoint.x - prePoint.x + viewPoint.x;
float dy = currPoint.y - prePoint.y - prePoint.y + viewPoint.y;
lParams.leftMargin = (int) (prePoint.x + dx);
lParams.topMargin = (int) (prePoint.y + dy);
view.setLayoutParams(lParams);
}
}