两个视图之间的自定义绘制线无法正常工作 [英] Custom Draw Line Between Two Views Not Working Properly

查看:60
本文介绍了两个视图之间的自定义绘制线无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在android中的按钮之间画线.我创建了一个自定义类,该类在相对布局内的按钮之间绘制线(相对布局是父布局).

I am trying to draw lines between buttons in android. I created one custom class which draw line between buttons inside relative layout(Relative Layout is parent layout).

这是我的MatchTheColoumnDrawView.java类,它接受上下文,startView,endView,lineColour,endCircleColour,厚度(以浮点数表示),方向(LEFT_TO_RIGHT或RIGHT_TO_LEFT).

Here is my MatchTheColoumnDrawView.java class which accepts context, startView, endView, lineColour, endCircleColour, thickness(in float), direction(LEFT_TO_RIGHT OR RIGHT_TO_LEFT).

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.Log;
import android.view.View;

/**
 * Created by Abhishek on 12/21/2016.
 *
 * Changes made as per new design on Date 9 Oct 2017
 *
 */

public class MatchTheColumnDrawView extends View {


    public static final int LEFT_TO_RIGHT = 1, RIGHT_TO_LEFT = 2;

    private Paint mLinePaint, mCirclePaint;

    private View startView, endView;

    private int direction;

    private Canvas canvas;

    private float dashWidth = 15f;

    private float dashGap = 8f;

    float[] intervals = new float[]{dashWidth, dashGap};

    float phase = 0;

    private int LINE_COLOR = Color.parseColor("#BEBEBE");

    private int END_CIRCLE_COLOR = Color.parseColor("#FF99CC00");

    /**
     *
     * parametrised constructor draws line from
     * @startView
     * to
     * @endView
     * as per
     * @direction
     * and
     * @lineColor
     * and
     * @endCircleColor
     *
     * when
     * @lineColor == null
     * default lineColor is gray
     *
     * when
     * @endCircleColor == null
     * default endCircleColor is green
     * */

    public MatchTheColumnDrawView(Context context,
                                  View startView, View endView,
                                  String lineColor, String endCircleColor,
                                  float thickness, int direction) {

        super(context);

        mLinePaint = new Paint();
        if (lineColor != null) LINE_COLOR = Color.parseColor(lineColor);
        mLinePaint.setColor(LINE_COLOR);
        mLinePaint.setStrokeWidth(thickness);
        mLinePaint.setStyle(Paint.Style.STROKE);

        mCirclePaint = new Paint();
        if (endCircleColor != null) END_CIRCLE_COLOR = Color.parseColor(endCircleColor);
        mCirclePaint.setColor(END_CIRCLE_COLOR);
        mCirclePaint.setStrokeWidth(thickness);

        this.startView = startView;
        this.endView = endView;
        this.direction = direction;

        //setBackgroundColor To Transparent
        super.setBackgroundColor(Color.TRANSPARENT);
    }

    public View getStartView() {
        return startView;
    }

    public void setStartView(View startView) {
        this.startView = startView;
    }

    public View getEndView() {
        return endView;
    }

    public void setEndView(View endView) {
        this.endView = endView;
    }

    public Canvas getCanvas() {
        return canvas;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        this.canvas = canvas;

        Log.d("Direction", String.valueOf(direction));

        Log.d("Start View Y:", String.valueOf(startView.getY()));
        Log.d("Start View H:", String.valueOf(startView.getHeight()));

        Log.d("End View Y:", String.valueOf(endView.getY()));
        Log.d("End View H:", String.valueOf(endView.getHeight()));

        //By default takes LEFT_TO_RIGHT

        if (direction == RIGHT_TO_LEFT) {

            //For RIGHT TO LEFT
            //Calculating Left X And Mid Of Height Y
            /*
            *                   ______________
            *                  |              |
            * This Point ==>> .|              |
            *                  |              |
            *                  |______________|
            * */

            float startViewLeftX = startView.getX();
            float startViewMidHeightY = startView.getY() + startView.getHeight() / 2;

            //Calculating Right X And Mid Of Height Y
            /*
            *     ______________
            *    |              |
            *    |              |.  <<== This Point
            *    |              |
            *    |______________|
            * */

            float endViewRightX = endView.getX() + endView.getWidth(); //20 is just to remove unwanted padding on Right Side
            float endViewMidHeightY = endView.getY() + endView.getHeight() / 2;

            Path mPath = new Path();

            mPath.moveTo(startViewLeftX, startViewMidHeightY);
            mPath.lineTo(endViewRightX, endViewMidHeightY);

            DashPathEffect dashPathEffect = new DashPathEffect(intervals, phase);

            mLinePaint.setPathEffect(dashPathEffect);

            canvas.drawPath(mPath, mLinePaint);

            //canvas.drawLine(startViewLeftX, startViewMidHeightY, endViewRightX, endViewMidHeightY, mLinePaint);
            canvas.drawCircle(startViewLeftX, startViewMidHeightY, 5, mCirclePaint);
            canvas.drawCircle(endViewRightX, endViewMidHeightY, 5, mCirclePaint);
        } else {

            //FOR LEFT_TO_RIGHT
            //Calculating Right X And Mid Of Height Y
            /*
            *     ______________
            *    |              |
            *    |              |.  <<== This Point
            *    |              |
            *    |______________|
            * */

            float startViewRightX = startView.getX() + startView.getWidth(); //20 is just to remove unwanted padding on Right Side
            float startViewMidHeightY = startView.getY() + startView.getHeight() / 2;

            //Calculating Left X And Mid Of Height Y
            /*
            *                   ______________
            *                  |              |
            * This Point ==>> .|              |
            *                  |              |
            *                  |______________|
            * */

            float endViewLeftX = endView.getX();
            float endViewMidHeightY = endView.getY() + endView.getHeight() / 2;

            Path mPath = new Path();

            mPath.moveTo(startViewRightX, startViewMidHeightY);
            mPath.lineTo(endViewLeftX, endViewMidHeightY);

            DashPathEffect dashPathEffect = new DashPathEffect(intervals, phase);

            mLinePaint.setPathEffect(dashPathEffect);

            canvas.drawPath(mPath, mLinePaint);
            //canvas.drawLine(startViewRightX, startViewMidHeightY, endViewLeftX, endViewMidHeightY, mLinePaint);
            canvas.drawCircle(startViewRightX, startViewMidHeightY, 5, mCirclePaint);
            canvas.drawCircle(endViewLeftX, endViewMidHeightY, 5, mCirclePaint);
        }
    }

    @Override
    public void setBackgroundColor(int color) {
        super.setBackgroundColor(color);
    }


}

我写了一个类MatchTheFollowingAttempted,它扩展了相对布局,它包含用于绘制按钮和它们之间的线条的逻辑.这是MatchTheFollowingAttempted.java类.

I wrote one class MatchTheFollowingAttempted which extend Relative layout and it contains logic for drawing buttons and lines between them. Here is MatchTheFollowingAttempted.java class.

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.RelativeLayout;

import java.util.ArrayList;

/**
 * Created by Abhishek on 24-10-2017.
 */

public class MatchTheFollowingAttempted extends RelativeLayout {

    private Context mContext;

    int numberOfOneSideButtons = 5;


    public MatchTheFollowingAttempted(Context context) {
        super(context);
        mContext = context;
        initialiseView();
    }

    public MatchTheFollowingAttempted(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        initialiseView();
    }

    public MatchTheFollowingAttempted(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        initialiseView();
    }

    public MatchTheFollowingAttempted(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mContext = context;
        initialiseView();
    }

    public void initialiseView() {

        ArrayList<Button> leftSideButtons = new ArrayList<>();
        ArrayList<Button> rightSideButtons = new ArrayList<>();
        ArrayList<MatchTheColumnDrawView> matchTheColumnDrawViewArrayList = new ArrayList<>();


        for (int i = 0; i < numberOfOneSideButtons; i++) {

            Button mButton = new Button(mContext);

            mButton.setId(View.generateViewId());

            RelativeLayout.LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

            if (i != 0) {

                layoutParams.addRule(BELOW, leftSideButtons.get(i-1).getId());

            }

            layoutParams.setMargins(10, 10, 10, 10);

            mButton.setLayoutParams(layoutParams);

            leftSideButtons.add(mButton);

            addView(mButton);


        }

        for (int i = 0; i < numberOfOneSideButtons; i++) {

            Button mButton = new Button(mContext);

            mButton.setId(View.generateViewId());

            RelativeLayout.LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

            layoutParams.addRule(ALIGN_PARENT_RIGHT);

            if (i != 0) {

                layoutParams.addRule(BELOW, rightSideButtons.get(i-1).getId());

            }

            layoutParams.setMargins(10, 10, 10, 10);

            mButton.setLayoutParams(layoutParams);

            rightSideButtons.add(mButton);

            addView(mButton);

        }

        for (int i = 0; i < numberOfOneSideButtons; i++) {

            MatchTheColumnDrawView matchTheColumnDrawView = new MatchTheColumnDrawView(mContext, leftSideButtons.get(i), rightSideButtons.get(4-i), null, null, 2.0f, MatchTheColumnDrawView.LEFT_TO_RIGHT);

            matchTheColumnDrawViewArrayList.add(matchTheColumnDrawView);

            addView(matchTheColumnDrawView);
        }



    }

}

当我直接在LinearLayout(Inside Scrollview)内使用MatchTheFollowingAttempted时,它将正确显示视图,其中包括按钮和线条.如图所示(Image_One).这是它的xml.

When I directly use MatchTheFollowingAttempted inside LinearLayout(Inside Scrollview) it will shows view properly with buttons and lines between them. As shown in image attached(Image_One). Here is the xml for it.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <MatchTheFollowingAttempted
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            </MatchTheFollowingAttempted>

        </LinearLayout>

    </ScrollView>

</RelativeLayout>

但是,当我在线性布局中添加第二个MatchTheFollowingAttempted时,将不会显示行(如所附图像Image_Two中所示).这是在线性布局中具有两个MatchTheFollowingAttempted的xml.

But when I add second MatchTheFollowingAttempted in Linear Layout Then Lines will not shown(As in attached image Image_Two). Here is xml with two MatchTheFollowingAttempted in Linear Layout.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <MatchTheFollowingAttempted
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            </MatchTheFollowingAttempted>

            <MatchTheFollowingAttempted
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

            </MatchTheFollowingAttempted>

        </LinearLayout>

    </ScrollView>

</RelativeLayout>

当我运行它时,它不会显示第二个布局.

When I run it real its not showing second layout.

推荐答案

当前,您正在以编程方式将MatchTheColumnDrawView s添加到MatchTheFollowingAttempted而不设置LayoutParams. LayoutParams有默认值,因此仅使用MatchTheFollowingAttempted的一个实例,您就很幸运.

Currently, you are adding MatchTheColumnDrawViews programmatically to MatchTheFollowingAttempted without setting LayoutParams. There are default values for LayoutParams, so with just one instance of MatchTheFollowingAttempted you were simply lucky.

因此,要解决此问题,必须将LayoutParams设置为MatchTheColumnDrawView,例如:

So to fix the problem you have to set LayoutParams to the MatchTheColumnDrawViews, for example like this:

for (int i = 0; i < numberOfOneSideButtons; i++) {

    MatchTheColumnDrawView matchTheColumnDrawView = new MatchTheColumnDrawView(
                       mContext, leftSideButtons.get(i), 
                       rightSideButtons.get(numberOfOneSideButtons - 1 - i),
                       null, null,
                       2.0f, MatchTheColumnDrawView.LEFT_TO_RIGHT);

    RelativeLayout.LayoutParams layoutParams = new LayoutParams(
                                      ViewGroup.LayoutParams.MATCH_PARENT, 
                                      ViewGroup.LayoutParams.MATCH_PARENT);
    layoutParams.addRule(ALIGN_TOP, 
                  leftSideButtons.get(0).getId());
    layoutParams.addRule(ALIGN_BOTTOM, 
                  leftSideButtons.get(numberOfOneSideButtons - 1).getId());
    matchTheColumnDrawView.setLayoutParams(layoutParams);

    matchTheColumnDrawViewArrayList.add(matchTheColumnDrawView);

    addView(matchTheColumnDrawView);
}

如果左侧和右侧Button的高度变化很大,那么您将不得不找到一个更好的公式,但是想法是要求尽可能多的空间,而不是整个屏幕(否则,下一个自定义根本不会显示),因此请尝试将MatchTheColumnDrawView底部与最低的Button底部对齐.

If your left side and right side Buttons have very varying heights then you will have to find a better formula but the idea is to request as much space as possible but not the whole screen (else the next custom View will not be shown at all), so try to align the MatchTheColumnDrawView bottom to the lowest Button bottom.

P.S.我怎么知道的?

P.S. How did I find out?

在您的View上使用Android Studio的布局检查器(顺便说一句,感谢您发布完整的代码!)显示,丢失"的View的高度实际上为0.

Using Android Studio's Layout Inspector on your View (BTW thanks for posting the full code!) showed that the "missing" Views actually had a height of 0.

覆盖MatchTheColumnDrawView中的onMeasure()以记录测量的宽度和高度,这确认了这一点:RelativeLayout进行了两次布局遍历:在第一次遍历之后可能会出现高度为0,但是在第二遍遍之后,View应该具有大于0的高度,否则将不会显示.

Overriding onMeasure() in MatchTheColumnDrawView to log the measured width and height confirmed this: RelativeLayout does two layout passes: having a height of 0 after the first pass may happen, but after the second pass the View should have some height > 0 or it will not be shown.

MatchTheFollowingAttempted中,为每个按钮设置一个填充 如果您在计算中间高度值(例如

In MatchTheFollowingAttempted, you set a padding to each of the Buttons The green circles will be drawn exactly in the middle of the View's edge if you subtract the padding when calculating the mid height values, e.g.

float endViewMidHeightY = endView.getY() + endView.getHeight() / 2 - 10;

因为Button具有其自己的填充(看起来比实际小),所以圆仍不与可见的Button边缘重叠.要实现此目的,请使用您自己的Button背景或(有点怪异,因为填充可能会在将来的Android版本中更改)从x坐标(左侧)减去填充,然后将填充添加到x坐标(右侧).

Because a Button has its own padding (it looks smaller than it actually is), the circles still do not overlap the visible Button edge. To achieve this, either use your own Button background or (kind of hacky because the padding may change in future Android versions) subtract the padding from the x coordinate (left side) respectively add the padding to the x coordinate (right side) .

下面的屏幕快照显示了一个示例,其中包含一些自定义View和一些Button:

The screenshot below shows an example with some custom Views and some Buttons:

这篇关于两个视图之间的自定义绘制线无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆