gpt4 book ai didi

android - 自定义 Android View 在线性和相对布局中显示不同

转载 作者:行者123 更新时间:2023-11-30 03:06:08 24 4
gpt4 key购买 nike

我创建了一个自定义 View (ArrowContainer) 来包裹其他元素,为它们提供箭头形状的背景。但是,当包含在相对布局中时,我的 View 显示方式与包含在线性布局中时的显示方式不同。

这就是问题所在,顶部的 ArrowContainer 包含在 LinearLayout 中并且行为正确,底部的 ArrowContainer 包含在 RelativeLayout 中并且行为不正确。

An image showing the problem

有没有人见过这样的东西?我在 ArrowContainer.java 中插入的调试代码表明问题是由 RelativeLayout 测量 View 两次引起的,但我不确定为什么这会导致问题...

代码如下:

ArrowContainer.java

package com.example.arrowcontainertest;


import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

public class ArrowContainer extends ViewGroup {

private static final int ARROW_LEFT = 0;
private static final int ARROW_RIGHT = 1;
private static final int ARROW_BOTH = 2;

private static final int DEFAULT_COLOUR = 0xFFFF0000;

private static final int HORIZONTAL_PADDING = 150;

private Path path;
private Paint paint;
private int arrowSide = ARROW_RIGHT;
private int colour = DEFAULT_COLOUR;
private int downColour;
private Paint downPaint;
private Boolean isButton = false;

private View child;

public ArrowContainer(Context context) {
super(context);
init();
}


public ArrowContainer(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArrowContainer, 0, 0);

try {
arrowSide = a.getInteger(R.styleable.ArrowContainer_arrowSide, ARROW_RIGHT);
colour = a.getColor(R.styleable.ArrowContainer_colour, DEFAULT_COLOUR);
isButton = a.getBoolean(R.styleable.ArrowContainer_isButton, false);
} finally {
a.recycle();
}

init();
}

public ArrowContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArrowContainer, 0, 0);

try {
arrowSide = a.getInteger(R.styleable.ArrowContainer_arrowSide, ARROW_RIGHT);
colour = a.getColor(R.styleable.ArrowContainer_colour, DEFAULT_COLOUR);
isButton = a.getBoolean(R.styleable.ArrowContainer_isButton, false);
} finally {
a.recycle();
}

init();
}

private void init() {
paint = new Paint();
paint.setColor(colour);
paint.setStyle(Style.FILL);
setWillNotDraw(false);

if (isButton) {
setFocusable(true);
setClickable(true);

downColour = 0xFF00FF00;
downPaint = new Paint();
downPaint.setColor(downColour);
downPaint.setStyle(Style.FILL);
}
}

@Override
protected void onFinishInflate() {
// Must have exactly 1 child
assert getChildCount()==1;
if (getChildCount() == 1) {
child = getChildAt(0);
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Debug
Log.e("DEBUG", "Type:" + getParent().getClass());
Log.e("DEBUG", "Width Mode: " + MeasureSpec.getMode(widthMeasureSpec));
Log.e("DEBUG", "Height Mode: " + MeasureSpec.getMode(heightMeasureSpec));
Log.e("DEBUG", "Width Size: " + MeasureSpec.getSize(widthMeasureSpec));
Log.e("DEBUG", "Height Size: " + MeasureSpec.getSize(heightMeasureSpec));


// Restrict the childs width to at most this components size minus a fixed value (HORIZONTAL_PADDING*numArrows)
int numArrows=0;
switch (arrowSide) {
case ARROW_RIGHT:
numArrows = 1;
break;
case ARROW_LEFT:
numArrows = 1;
break;
case ARROW_BOTH:
numArrows = 2;
break;
}
int widthSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec)-HORIZONTAL_PADDING*numArrows, MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
child.measure(widthSpec, heightSpec);
int width = child.getMeasuredWidth();
int height = child.getMeasuredHeight();
setMeasuredDimension(width + (int) (numArrows*height/2f), height);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
switch (arrowSide) {
case ARROW_RIGHT:
// Hug left
child.layout(0, height/2 - childHeight/2, width - height/2, height/2 + childHeight/2);
break;
case ARROW_LEFT:
// Hug right
child.layout(height/2, height/2 - childHeight/2, width, height/2 + childHeight/2);
break;
case ARROW_BOTH:
// Center
child.layout(width/2 - childWidth/2, height/2 - childHeight/2, width/2 + childWidth/2, height/2 + childHeight/2);
break;
}
}


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
path = new Path();
switch (arrowSide) {
case ARROW_RIGHT:
path.lineTo(0, h);
path.lineTo(w-h/2f, h);
path.lineTo(w, h/2f);
path.lineTo(w-h/2f, 0);
break;
case ARROW_LEFT:
path.moveTo(h/2f, 0);
path.lineTo(0, h/2f);
path.lineTo(h/2f, h);
path.lineTo(w, h);
path.lineTo(w, 0);
break;
case ARROW_BOTH:
path.moveTo(h/2f, 0);
path.lineTo(0, h/2f);
path.lineTo(h/2f, h);
path.lineTo(w-h/2f, h);
path.lineTo(w, h/2f);
path.lineTo(w-h/2f, 0);
break;
}
path.close();
}

@Override
protected void onDraw(Canvas canvas) {
invalidate();
if (isPressed()) {
canvas.drawPath(path, downPaint);
} else {
canvas.drawPath(path, paint);
}
super.onDraw(canvas);
}

}

activity_main.xml

<LinearLayout 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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" xmlns:app="http://schemas.android.com/apk/res/com.example.arrowcontainertest">

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

<com.example.arrowcontainertest.ArrowContainer
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:arrowSide="right">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play"
android:textSize="50sp"/>
</com.example.arrowcontainertest.ArrowContainer>

</LinearLayout>

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

<com.example.arrowcontainertest.ArrowContainer
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:arrowSide="right">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play"
android:textSize="50sp"/>
</com.example.arrowcontainertest.ArrowContainer>

</RelativeLayout>

主 Activity .java

package com.example.arrowcontainertest;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

}

最佳答案

更新:

我一直无法解决这个问题,而且这个组件在其他情况下也引起了问题。因此,我决定重写组件以尽可能使用一些自定义功能。

我的解决方案是创建一个包含嵌套 LinearLayout 的自定义 LinearLayout。外部布局负责绘制背景,并应用足够的填充以留出空间来绘制箭头。所有 child 都被传递到内部布局。此解决方案并不完美,因为通常会出现过多的填充并因此浪费空间,但它足以满足我的目的。

代码在这里:

package com.example.arrowcontainertest;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

public class NewArrowContainer extends LinearLayout {

private static final int ARROW_LEFT = 0;
private static final int ARROW_RIGHT = 1;
private static final int ARROW_BOTH = 2;
private static final int DEFAULT_COLOUR = 0xFFFF0000;
private static final int ARROW_MAX_WIDTH = 150;

private LinearLayout childLayout;

private Path path;
private Paint paint;
private int arrowSide = ARROW_RIGHT;
private int colour = DEFAULT_COLOUR;
private int downColour;
private Paint downPaint;
private Boolean isButton = false;

public NewArrowContainer(Context context) {
super(context);
init();
}

public NewArrowContainer(Context context, AttributeSet attrs) {
super(context, attrs);

TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArrowContainer, 0, 0);

try {
arrowSide = a.getInteger(R.styleable.ArrowContainer_arrowSide, ARROW_RIGHT);
colour = a.getColor(R.styleable.ArrowContainer_colour, DEFAULT_COLOUR);
isButton = a.getBoolean(R.styleable.ArrowContainer_isButton, false);
} finally {
a.recycle();
}

init();
}

public NewArrowContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);

TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArrowContainer, 0, 0);

try {
arrowSide = a.getInteger(R.styleable.ArrowContainer_arrowSide, ARROW_RIGHT);
colour = a.getColor(R.styleable.ArrowContainer_colour, DEFAULT_COLOUR);
isButton = a.getBoolean(R.styleable.ArrowContainer_isButton, false);
} finally {
a.recycle();
}

init();
}

private void init() {
paint = new Paint();
paint.setColor(colour);
paint.setStyle(Style.FILL);
setWillNotDraw(false);

if (isButton) {
setFocusable(true);
setClickable(true);

downColour = 0xFF00FF00;
downPaint = new Paint();
downPaint.setColor(downColour);
downPaint.setStyle(Style.FILL);
}

LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.arrow_container, this);
childLayout = (LinearLayout) findViewById(R.id.child);

// Pass properties to childLayout
childLayout.setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
childLayout.setOrientation(getOrientation());

// Give the padding sufficient for arrows
switch (arrowSide) {
case ARROW_RIGHT:
setPadding(0, 0, ARROW_MAX_WIDTH, 0);
break;
case ARROW_LEFT:
setPadding(ARROW_MAX_WIDTH, 0, 0, 0);
break;
case ARROW_BOTH:
setPadding(ARROW_MAX_WIDTH, 0, ARROW_MAX_WIDTH, 0);
break;
}
}

public void setColour(int colour) {
paint.setColor(colour);
}

@Override
public void onFinishInflate() {
// Pass all children to the childLayout
while (getChildCount() > 1) {
View v = getChildAt(1);
removeViewAt(1);
childLayout.addView(v);
}
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
path = new Path();
switch (arrowSide) {
case ARROW_RIGHT:
path.lineTo(0, h);
path.lineTo(w-ARROW_MAX_WIDTH, h);
path.lineTo(w-ARROW_MAX_WIDTH+h/2f, h/2f);
path.lineTo(w-ARROW_MAX_WIDTH, 0);
break;
case ARROW_LEFT:
path.moveTo(ARROW_MAX_WIDTH-h/2f, h/2f);
path.lineTo(ARROW_MAX_WIDTH, h);
path.lineTo(w, h);
path.lineTo(w, 0);
path.lineTo(ARROW_MAX_WIDTH, 0);
break;
case ARROW_BOTH:
path.moveTo(ARROW_MAX_WIDTH-h/2f, h/2f);
path.lineTo(ARROW_MAX_WIDTH, h);
path.lineTo(w-ARROW_MAX_WIDTH, h);
path.lineTo(w-ARROW_MAX_WIDTH+h/2f, h/2f);
path.lineTo(w-ARROW_MAX_WIDTH, 0);
path.lineTo(ARROW_MAX_WIDTH, 0);
break;
}
path.close();
}

@Override
protected void onDraw(Canvas canvas) {
invalidate();
if (isPressed()) {
canvas.drawPath(path, downPaint);
} else {
canvas.drawPath(path, paint);
}
super.onDraw(canvas);
}

}

关于android - 自定义 Android View 在线性和相对布局中显示不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21801188/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com