10

我正在尝试以编程方式(不使用 XML 文件)在 Android 中创建自定义子视图(这就是我在 iOS 中所称的),这基本上是将许多基本视图(标签、按钮、文本字段等)组合成一个可重用的子视图类,所以我可以在我的内部UIViewControllersActivityAndroid 中使用它。

我不知道 Android 中的正确术语是什么。似乎有一百万种不同的术语。

自定义视图、视图组、布局、小部件、组件,无论你想怎么称呼它。

在 iOS 中,这很简单:

自定义视图.h

@interface CustomView : UIView

@property (nonatomic, strong) UILabel *message;
@property (nonatomic, strong) UIButton *button;

@end

自定义视图.m

@implementation CustomView

-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if(self)
    {
        [self initViews];
        [self initConstraints];
    }

    return self;
}

-(void)initViews
{
    self.message = [[UILabel alloc] init];
    self.button = [[UIButton alloc] init];

    [self addSubview:self.message];
    [self addSubview:self.button];
}

-(void)initConstraints
{
    id views = @{
                 @"message": self.message,
                 @"button": self.button
                 };

    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[message]|" options:0 metrics:nil views:views]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[button]|" options:0 metrics:nil views:views]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[message][button]|" options:0 metrics:nil views:views]];
}

@end

Activity现在我可以在我选择的任何 ViewController (Android) 中重用这个自定义视图。

一个人如何在 Android 中实现这样的目标?

我一直在环顾四周,从我在 Android 中收集的内容中,为了添加子视图,我将它们添加到Layouts

RelativeLayout relativeLayout = new RelativeLayout(...);
TextView textView = new TextView(...);

relativeLayout.addSubview(textView);

这是否意味着我需要扩展RelativeLayoutViewGroup

看这个页面:http: //developer.android.com/reference/android/view/ViewGroup.html

似乎我们需要编写一些非常复杂的逻辑来布局自定义视图,例如:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();

        // These keep track of the space we are using on the left and right for
        // views positioned there; we need member variables so we can also use
        // these for layout later.
        mLeftWidth = 0;
        mRightWidth = 0;

        // Measurement will ultimately be computing these values.
        int maxHeight = 0;
        int maxWidth = 0;
        int childState = 0;

        // Iterate through all children, measuring them and computing our dimensions
        // from their size.
        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                // Measure the child.
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

                // Update our size information based on the layout params.  Children
                // that asked to be positioned on the left or right go in those gutters.
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (lp.position == LayoutParams.POSITION_LEFT) {
                    mLeftWidth += Math.max(maxWidth,
                            child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                } else if (lp.position == LayoutParams.POSITION_RIGHT) {
                    mRightWidth += Math.max(maxWidth,
                            child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                } else {
                    maxWidth = Math.max(maxWidth,
                            child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                }
                maxHeight = Math.max(maxHeight,
                        child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
            }
        }

        // Total width is the maximum width of all inner children plus the gutters.
        maxWidth += mLeftWidth + mRightWidth;

        // Check against our minimum height and width
        maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

        // Report our final dimensions.
        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                resolveSizeAndState(maxHeight, heightMeasureSpec,
                        childState << MEASURED_HEIGHT_STATE_SHIFT));
    }

我要做的就是在自定义视图中使用多个基本的 android 标签、视图、按钮,如上面的 iOS 示例,为什么在 Android 中这么难?

我希望有这样简单的东西:

public class CustomView extends View
{
    public RelativeLayout mainLayout;

    public TextView message;
    public Button button;

    // default constructor
    public CustomView()
    {
        ...

        initViews();
        initLayouts();
        addViews();
    }

    public initViews()
    {
        mainLayout = new RelativeLayout(this);

        message = new TextView(this);

        button = new Button(this);

        ...
    }

    public initLayouts()
    {
        // --------------------------------------------------
        // use Android layout params to position subviews 
        // within this custom view class
        // --------------------------------------------------
    }

    public addViews()
    {
        mainLayout.addView(message);
        mainLayout.addView(button);

        setContentView(mainLayout);  
    }
}

对不起,我真诚地尝试学习和构建一个基本的 Android 应用程序,而不是试图抨击 Android 的做事方式。

我知道如何在 Activity 中添加和布局子视图,并且过去两天一直在这样做,但不是在自定义视图/视图组/布局中。我不想最终为我在 Android 应用程序中的每个 Activity 构建完全相同的子视图,这违反了良好的编码习惯,对吗?:D

在这里只需要一些做过 iOS 和 Android 开发的人的指导。

编辑

似乎我正在寻找的东西被称为复合控件:http: //developer.android.com/guide/topics/ui/custom-components.html

我会继续挖掘,希望能达到我想要的结果:D

只需要解决这个充气机业务。

4

3 回答 3

1

好的,我想我明白了,不确定它是否是最好的解决方案,但它可以满足我的需求。

所以它是这样的:

public class CustomView extends RelativeLayout
{
    private Context context;

    public TextView message;
    public Button button;

    public CustomView(Context context)
    {
        super(context);

        // ---------------------------------------------------------
        // store context as I like to create the views inside 
        // initViews() method rather than in the constructor
        // ---------------------------------------------------------
        this.context = context;

        initViews();
        initLayouts();
        addViews();
    }

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

        // ---------------------------------------------------------
        // store context as I like to create the views inside 
        // initViews() method rather than in the constructor
        // ---------------------------------------------------------
        this.context = context;

        initViews();
        initLayouts();
        addViews();
    }

    public initViews()
    {
        // ----------------------------------------
        // note "context" refers to this.context
        // that we stored above.
        // ----------------------------------------
        message = new TextView(context);
        ...

        button = new Button(context);
        ...

    }

    public initLayouts()
    {
        // --------------------------------------------------
        // use Android layout params to position subviews 
        // within this custom view class
        // --------------------------------------------------

        message.setId(View.generateViewId());
        button.setId(View.generateViewId());

        RelativeLayout.LayoutParams messageLayoutParams = new RelativeLayout.LayoutParams(
                LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT
        );

        message.setLayoutParams(messageLayoutParams);


        RelativeLayout.LayoutParams buttonLayoutParams = new RelativeLayout.LayoutParams(
                LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT
        );

        button.setLayoutParams(buttonLayoutParams);
    }

    public addViews()
    {
        // adding subviews to layout
        addView(message);
        addView(button); 
    }
}

现在我可以在我的任何 Activity 中使用这个自定义视图:

public class MainActivity extends ActionBarActivity {

    // custom view instance
    protected CustomView approvalView;

    @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ...
            initViews();
        }

        public initViews()
        {
            ...

            approvalView = new CustomView(this);
            approvalView.message.setText("1 + 1 = 2");
            approvalView.button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Log.d("Logger", "Math formula approved! :D");
                }
            });

        }
}

如果我们使用 XML 创建布局,则使用 Inflater,这不是我喜欢做的事情,所以我以编程方式生成了视图的布局:D

上面“扩展RelativeLayout”中的“RelativeLayout”当然可以替换为“LinearLayout”或者其他布局。

于 2015-05-10T14:05:01.523 回答
1

为这个问题的普通访客添加一个简单的答案......

您不能View像使用 iOS 那样向 Android 添加子视图UIView

如果您需要在 Android 中添加子视图,请使用其中一个ViewGroup子类(如LinearLayoutRelativeLayout,甚至您自己的自定义子类)。

myViewGroup.addView(myView);
于 2017-05-18T11:29:00.843 回答
0

Android 和 ios 应用程序开发是两个不同的概念,每个都有自己的方式来执行您的任务。有时很难在 android 中开发一段代码,有时在 ios 中。要在 android 中创建视图屏幕/设计/GUI,您可以创建 XML 文件(推荐)或通过代码(这在某种程度上难以维护 wrt XML)。

对于您的问题,您不需要创建自己的问题,或者ViewGroup例如,如果您想用作视图的父级,而不是使用 XML,您可以使用。RelativeLayoutLinearLayoutRelativeLayout

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Some Text"/>
</RelativeLayout>

如果您想务实地创建视图而不是使用

    RelativeLayout parentRelativeLayout = new RelativeLayout(context);
    RelativeLayout.LayoutParams parentParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    parentRelativeLayout.setLayoutParams(parentParams);
    TextView childTextView = new TextView(context);
    childTextView.setText("Some Text");
    mRelativeLayout.addView(childTextView);    

它只是一个示例代码,两者都具有相同的输出,但是使用编程方法,随着您的视图的增长,这将变得很困难。

查看您的代码,您正在android中创建自定义视图(为什么?),如果默认视图没有提供我们需要在代码中使用/实现的某些功能,我们只需要自定义视图。据我了解,您想使用自定义视图进行重用。这是一个很好的方法,但如果 android 提供了一些功能,而不是你试图再次发明轮子的原因,只需使用不同的布局,如果你想要额外的东西,只使用自定义视图。

于 2015-05-10T11:37:56.057 回答