Пользовательские экземпляры вида, программно добавленные в пользовательский держатель, имеют неправильную позицию и размер - PullRequest
0 голосов
/ 24 мая 2018

У меня есть пользовательское представление (расширение View), группа пользовательских представлений (расширение LinearLayout), в которой должно храниться несколько экземпляров моего пользовательского представления.Инициализация происходит в течение Fragment.Я хотел бы добиться, чтобы экземпляры пользовательских представлений были выровнены горизонтально в моей группе пользовательских представлений (в конце концов она должна стать многослайдерной).Чего я достиг на данный момент: я могу добавить свои пользовательские экземпляры вида в свою пользовательскую группу представлений, но они отображаются неправильно - первый экземпляр (вертикальная полоса) отображается правильно, последующие столбцы не расположены ни правильно, ни неправильноширина (см. скриншот).

custom view group with

Из запросов значений, таких как getX(), getWidth(), getLeft(), getRight() для моего пользовательского представленияВ некоторых случаях я не получаю никаких намеков на то, что не так - сообщенные значения указывают, что ползунки были правильно расположены и расположены.


Здесь задействованы 3 пользовательских класса:

Фрагмент

public class MultiSliderFragment extends MSBaseFragment {
    private final static String TAG = "MultiSliderFragment";
    private MultiSliderView mMSView;

    public MultiSliderFragment() {
        super();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View mMSContainer = inflater.inflate(R.layout.multislider_view, container, false);
        mMSView = (MultiSliderView) mMSContainer.findViewById(R.id.multislider_view);
        Bundle numsBundle = this.getArguments();
        ArrayList<Integer> sliderNums = numsBundle.getIntegerArrayList("nums");

        ArrayList<SliderBar> sliders = new ArrayList<>();
        assert sliderNums != null;
        for (int num : sliderNums) {
            SliderBar bar = new SliderBar(getActivity());
            bar.setNum(String.valueOf(num));
            sliders.add(bar);
        }

        int x = 0;
        for (SliderBar slider : sliders) {
            mMSViewLeft.addView(slider);
        }

        // sliders have to know their number
        // and the container view (MultiSliderView)
        // needs  know the dimensions of the screen
        setSliderProps(sliderNums);

        return mMSContainer;
    }

    private void setSliderProps(ArrayList<Integer> sliderNums) {
        MSApplication app = (MSApplication) getActivity().getApplication();
        Point screenDimensions = app.getDimensions();
        mMSView.setScreenDimensions(screenDimensions);
        mMSView.setSliderNums(sliderNums);
    }
}

ViewGroup, содержащая ползунки

public class MultiSliderView extends LinearLayout {
    final static private String TAG = "MultiSliderView";
    private ArrayList<Integer> sliderNums;
    private Point screenDimensions;

    public MultiSliderView(Context context) {
        super(context);
        init(null, 0);
    }

    public MultiSliderView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public MultiSliderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr);
    }

    private void init(AttributeSet attrs, int defStyleAttr) {
        this.setOrientation(LinearLayout.HORIZONTAL);
    }

    public void setSliderNums(ArrayList<Integer> sliderNums) {
        this.sliderNums = sliderNums;
    }

    public void setScreenDimensions(Point dimensions) {
        this.screenDimensions = dimensions;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int desiredWidth = getSuggestedMinimumWidth() + getPaddingLeft() + getPaddingRight();
        int desiredHeight = getSuggestedMinimumHeight() + getPaddingTop() + getPaddingBottom();

        int measureWidth = measureDimension(desiredWidth, widthMeasureSpec);
        int measureHeight = measureDimension(desiredHeight, heightMeasureSpec);
        setMeasuredDimension(measureWidth, measureHeight);
    }

    private int measureDimension(int desiredSize, int measureSpec) {
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = desiredSize;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }

        if (result < desiredSize) {
            Log.e(TAG, "The view is too small, the content might get cut");
        }
        return result;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        Log.d(TAG, "MultiSliderView on layout: " + left + ", " + top + ", " + right + ", " + bottom);
        int barWidth = getMeasuredWidth()/sliderNums.size();
        int barHeight = getMeasuredHeight();
        int x = 0;
        for (int i = 0; i < getChildCount(); i++) {
            SliderBar child = (SliderBar) getChildAt(i);
            child.layout(x, 0, x + barWidth, barHeight);
            // increment x by barWidth, otherwise bars are laid out
            // on top of each other, each at position 0 within MultiSliderView
            x += barWidth;
        }
    }

    // manual interaction with the multislider (stub)
    // must report to the regarding SliderBar instance to redraw the slider
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        performClick();
        this.getParent().requestDisallowInterceptTouchEvent(true);

        int tempTouchX = (int) event.getX();
        int tempTouchY = (int) event.getY();

        Log.d(TAG, "touch position: " + tempTouchX + ", " + tempTouchY);
        invalidate();
        return true;
    }

    @Override
    public boolean performClick() {
        super.performClick();
        return false;
    }

Ползунок

public class SliderBar extends View {

    final static String TAG = "SliderBar";
    Paint mPaint;
    Canvas mCanvas;
    String pixelNum;
    Typeface typeFace = Typeface.create("sans-serif-light", Typeface.NORMAL);
    int left, top, right, bottom;
    Rect mArea = new Rect(left, top, right, bottom);
    int touchY;

    public SliderBar(Context context) {
        super(context);
        init(null, 0);
    }

    public SliderBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public SliderBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mCanvas = new Canvas();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.d(TAG, "slider bar on draw: " + left + ", " + top + ", " + right + ", " + bottom);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setColor(0x66000000);
        if (touchY <= top) touchY = top;
        if (touchY > bottom) touchY = bottom;
        canvas.drawRect(left, touchY, right, bottom, mPaint);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setTypeface(typeFace);
        mPaint.setTextSize((float) 30);
        mPaint.setColor(0xffffffff);
        canvas.drawText(pixelNum, right/2, bottom - 20, mPaint);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        this.setLeft(left);
        this.setTop(top);
        this.setRight(right);
        this.setBottom(bottom);
        // reports the right values but sliders aren't positioned correctly
        Log.d(TAG, "slider position: " + this.getLeft() + ", " + this.getRight());
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }

    public void setNum(String num) {
        this.pixelNum = num;
    }
}

Последнее, но не менее важное: макет, содержащий MultiSliderView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/multislider_view"
          android:orientation="horizontal"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:animateLayoutChanges="true"
          android:baselineAligned="false">

<net.myapp.views.MultiSliderView
    android:id="@+id/multislider_view_left"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginBottom="10dp"
    android:layout_marginEnd="5dp"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="5dp"
    android:layout_marginStart="10dp"
    android:layout_marginTop="10dp"
    android:background="@android:color/holo_blue_dark"
    android:orientation="horizontal"/>

</LinearLayout>

Я думал, что MultiSliderView наследовать от LinearLayout позволит мне размещать мои ползунки в горизонтальном ряду, даже не давая им горизонтальное положение.Однако это было не так - просто установив их позицию x в 0, просто расположите их друг над другом в позиции 0. Есть ли что-то очевидное, что я сделал неправильно при позиционировании ползунка?

Спасибо

1 Ответ

0 голосов
/ 25 мая 2018

Это то, что я получаю, используя ваш код и исправляя его в некоторых местах (getDimension () в классе Application вернет widthPixels и heightPixels текущего Window, используяDisplayMetrics):

enter image description here

Мои изменения на данный момент:

1 Иногда я получаю NPE вonLayout() из MultiSliderView, потому что View работает и работает до того, как переданы числа ArrayList. Поэтому я добавил null check onLayout(), а также invalidate(); в качестве последней строки в setSliderNums()

2 Кажется, SliderBar расположены правильно (проверено с помощью LayoutInspector), но большинство чисел не видны.Я полагаю, они должны появиться в середине их SliderBar (если я ошибаюсь, просто пропустите этот пункт).

Ваш код для рисования числа:

canvas.drawText (pixelNum, right / 2, bottom - 20, mPaint);

Это выиграноне работает, потому что значения left, top и т. д. подразумеваются относительно родителя ViewGroup, но вы рисуете Canvas в координатах относительно дочернего элемента View.Поэтому я изменил строку на

canvas.drawText(pixelNum, (right - left)/2, bottom - 20, mPaint);

3 Точно так же кажется, что setX() не работает так, как вы ожидаете («просто установив их позицию х в 0, просто позиционируем ихдруг на друге в позиции 0 "), поэтому позвольте мне объяснить, что он делает:

Значения, используемые с setX() и setY(), означают родительский элемент ViewGroup, содержащий View в вопросе.Поэтому, если вы скажете view.setX(0);, View будет нарисовано по левому краю родительского элемента ViewGroup.

4 В onLayout() из SliderBar это ненеобходимо установить значения left, top, ... путем вызова this.setLeft(left); и т. д. В этом методе View информируется об изменениях, которые уже были внесены в эти переменные.Поэтому я пропустил четыре лишние строки.

5 Я не совсем понимаю, чего вы пытаетесь достичь с помощью следующих двух строк из onDraw() в SliderBar:

if (touchY <= вверху) touchY = top; </p>

if (touchY> внизу) touchY = bottom;

В основном все, что они делают, установленозначение от touchY до внизу , если touchY > bottom в начале (при условии, что top > bottom) и оставление touchY в одиночку, если touchY <= bottom в начале.

Как сейчас обстоят дела, результирующий прямоугольник в большинстве случаев довольно одномерный.

Итак, что я хотел бы знать, так это то, что должно произойти в этой точке.

...