如何在不同的屏幕分辨率下重新定位视图上的点?

4

我在视图上绘制了一组点,如下图所示。

详情:

我有一组x和y坐标的数组。但问题是它不能根据不同的屏幕分辨率进行调整。

我想要什么:

我希望它能够独立于屏幕分辨率,在视图中心位置。

这是我的代码:

public class FirstFragment extends Fragment implements Step {

    int[] y = {381, 379, 372, 351, 329, 305, 269, 230, 195, 156, 117, 95, 64, 44, 24, 15, 13, 25, 46, 72, 107, 128, 162, 195, 222, 244, 266, 298, 324, 341, 363, 372, 378, 376, 363, 339, 308, 281, 263, 246, 237, 235, 244, 259, 274, 298, 318, 334, 353, 383, 414, 443, 467, 490, 512, 538, 562, 582, 596, 604, 602, 586, 562, 532, 510, 485, 461, 439, 414, 391, 365, 344, 322, 305, 262, 246, 235, 234, 241, 257, 279, 306, 335, 359, 375, 381};
    int[] x = {93, 109, 130, 157, 181, 207, 253, 297, 336, 380, 406, 413, 415, 405, 381, 358, 326, 296, 272, 261, 266, 274, 301, 336, 368, 397, 423, 461, 487, 508, 540, 562, 587, 613, 639, 658, 666, 659, 647, 629, 607, 583, 559, 527, 496, 461, 444, 425, 406, 375, 339, 313, 292, 274, 263, 261, 269, 284, 304, 332, 360, 395, 413, 421, 418, 403, 382, 360, 339, 304, 276, 253, 229, 207, 164, 136, 105, 79, 53, 31, 18, 13, 19, 35, 58, 93};

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.first_fragment, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        final ConnectDotsView connectDotsView = (ConnectDotsView) view.findViewById(R.id.connect_dots_view);

        connectDotsView.setOnCompleteListener(new ConnectDotsView.CompleteListener() {
            @Override
            public void onCompleteListener() {
                Toast.makeText(getActivity(), "Completed", Toast.LENGTH_SHORT).show();
                Log.e("Done", "true");
            }
        });

        connectDotsView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect rect = new Rect();
                connectDotsView.getLocalVisibleRect(rect);
                Log.e("Width", rect.width() + "");
                Log.e("Height", rect.height() + "");
                Log.e("left", rect.left + "");
                Log.e("right", rect.right + "");
                Log.e("top", rect.top + "");
                Log.e("bottom", rect.bottom + "");

                int scale = (int) getResources().getDisplayMetrics().density;

                List<Point> points = new ArrayList<>();
                for (int i = 0, j = 0; i < x.length && j < y.length; i++, j++) {
                    Point p = new Point(x[i] / scale + rect.left, y[j] / scale + rect.top);
                    points.add(p);
                }
                connectDotsView.setPoints(points);
            }
        });
    }

    @Override
    public VerificationError verifyStep() {
        return null;
    }

    @Override
    public void onSelected() {

    }

    @Override
    public void onError(@NonNull VerificationError error) {

    }

}

自定义视图类:

package com.sagar.quizdemo;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Displaying canvas with optional dots drawn on it. User can connect dots with
 * straight lines. First dot can be connected with second dot, second with third
 * etc.
 *
 * @author lecho
 */
public class ConnectDotsView extends View {

    private Bitmap mBitmap;
    private Canvas mCanvas;
    private Path mPath;
    private Paint mPaint;
    private static final int TOUCH_TOLERANCE_DP = 24;
    private static final int BACKGROUND = 0xFFDDDDDD;
    // Points to be connected.
    private List<Point> mPoints = new ArrayList<>();
    private int mLastPointIndex = 0;
    private int mTouchTolerance;
    private boolean isPathStarted = false;
    CompleteListener completeListener;

    public ConnectDotsView(Context context) {
        super(context);
        mCanvas = new Canvas();
        mPath = new Path();
        initPaint();
    }

    interface CompleteListener {
        void onCompleteListener();
    }

    public void setOnCompleteListener(CompleteListener listener) {
        completeListener = listener;
    }

    public ConnectDotsView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mCanvas = new Canvas();
        mPath = new Path();
        initPaint();
    }

    public ConnectDotsView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mCanvas = new Canvas();
        mPath = new Path();
        initPaint();
    }

    public void clear() {
        mBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        mBitmap.eraseColor(BACKGROUND);
        mCanvas.setBitmap(mBitmap);
        invalidate();
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
        super.onSizeChanged(width, height, oldWidth, oldHeight);
        clear();

    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(BACKGROUND);
        canvas.drawBitmap(mBitmap, 0, 0, null);
        canvas.drawPath(mPath, mPaint);

        // TODO remove if you don't want points to be visible.
        for (Point point : mPoints) {
            canvas.drawPoint(point.x, point.y, mPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touch_start(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touch_move(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touch_up(x, y);
                invalidate();
                break;
        }
        return true;
    }

    private void touch_start(float x, float y) {

        if (checkPoint(x, y, mLastPointIndex)) {
            mPath.reset();
            // User starts from given point so path can be drawn.
            isPathStarted = true;
        } else {
            // User starts move from point which does not belong to mPoints list
            isPathStarted = false;
        }

    }

    private void touch_move(float x, float y) {
        if (isPathStarted) {
            mPath.reset();
            Point point = mPoints.get(mLastPointIndex);
            mPath.moveTo(point.x, point.y);
            if (checkPoint(x, y, mLastPointIndex + 1)) {
                point = mPoints.get(mLastPointIndex + 1);
                mPath.lineTo(point.x, point.y);
                mCanvas.drawPath(mPath, mPaint);
                mPath.reset();
                ++mLastPointIndex;
            } else {
                int positionIndex = mLastPointIndex + 1;
                if (positionIndex >= mPoints.size()) {
                    completeListener.onCompleteListener();
                } else {
                    mPath.lineTo(x, y);
                }
            }
        }
    }

    private void touch_up(float x, float y) {
        mPath.reset();
        if (checkPoint(x, y, mLastPointIndex + 1) && isPathStarted) {
            // Move finished at valid point so I draw whole line.
            // That's the start point of current line segment.
            Point point = mPoints.get(mLastPointIndex);
            mPath.moveTo(point.x, point.y);
            // And that's the end point.
            point = mPoints.get(mLastPointIndex + 1);
            mPath.lineTo(point.x, point.y);
            mCanvas.drawPath(mPath, mPaint);
            mPath.reset();
            // Increment point index.
            ++mLastPointIndex;
            isPathStarted = false;
        }

    }

    /**
     * Checks if user touch point with some tolerance
     */
    private boolean checkPoint(float x, float y, int pointIndex) {
        if (pointIndex >= mPoints.size()) {
            // All dots already connected.
            return false;
        }
        Point point = mPoints.get(pointIndex);
        if (x > (point.x - mTouchTolerance) && x < (point.x + mTouchTolerance)) {
            if (y > (point.y - mTouchTolerance) && y < (point.y + mTouchTolerance)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Sets up paint attributes.
     */
    private void initPaint() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(12);
        mTouchTolerance = dp2px(TOUCH_TOLERANCE_DP);
    }

    /**
     * Converts dpi units to px
     *
     * @param dp
     * @return
     */
    private int dp2px(int dp) {
        Resources r = getContext().getResources();
        float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
        return (int) px;
    }

    public void setPaint(Paint paint) {
        this.mPaint = paint;
    }

    public Bitmap getBitmap() {
        return mBitmap;
    }

    public List<Point> getPoints() {
        return mPoints;
    }

    public void setPoints(List<Point> points) {
        this.mPoints = points;
    }
}

图片链接https://istack.dev59.com/n67mq.webp

请帮助我解决这个问题。


你可以分享一下目前的代码,也许会更好运。 - Neo
1
请查看更新后的问题。 - sagar suri
1个回答

1
你可以使用以下计算方法在中心绘制,
connectDotsView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            Rect rect = new Rect();

            // Fist find the min and max value for x axis
            int minX = x[0];
            int maxX = x[0];

            for (int i = 1; i <= x.length - 1; i++) {
                if (maxX < x[i]) {
                    maxX = x[i];
                }

                if (minX > x[i]) {
                    minX = x[i];
                }
            }

            // Find min and max vlaue for Y axis
            int minY = y[0];
            int maxY = y[0];

            for (int i = 1; i <= y.length - 1; i++) {
                if (maxY < y[i]) {
                    maxY = y[i];
                }

                if (minY > y[i]) {
                    minY = y[i];
                }
            }

            connectDotsView.getLocalVisibleRect(rect);
            Log.e("Width", rect.width() + "");
            Log.e("Height", rect.height() + "");
            Log.e("left", rect.left + "");
            Log.e("right", rect.right + "");
            Log.e("top", rect.top + "");
            Log.e("bottom", rect.bottom + "");

            // Find the scale factor based on the view you allocated in the screen
            float scaleX = ((float) ((float) rect.width() / (float) maxX));
            float scaleY = ((float) ((float) rect.height() / (float) maxY));

            final float scale;

            // Take the lowest scale factor
            if (scaleX > scaleY) {
                scale = scaleY;
            } else {
                scale = scaleX;
            }

            // find the left and top
            int left = (rect.width() - ((int) ((float) maxX * scale)) - ((int) ((float) minX * scale))) / 2;
            int top = (rect.height() - ((int) ((float) maxY * scale)) - ((int) ((float) minY * scale))) / 2;

            // base on the above calculation draw in a view
            List<Point> points = new ArrayList<>();
            for (int i = 0, j = 0; i < x.length && j < y.length; i++, j++) {
                Point p = new Point(((int) ((float) x[i] * scale)) + left, (int) ((float) y[j] * scale) + top);
                points.add(p);
            }
            connectDotsView.setPoints(points);
        }
    });

输出将会是这样的:

enter image description here


太棒了!它完美地运行。希望在不同的屏幕分辨率下它的位置不会改变。这会根据设备的屏幕分辨率进行绘制吗? - sagar suri
不会改变位置,因为我的计算是基于视图大小和您提供的点数。 - Muthukrishnan Rajendran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接