创建 Android 个子视图,例如 iOS 个子视图

Create Android subviews like iOS subviews

我正在尝试编程(不使用XML文件)在Android中创建自定义子视图(这就是我在[=85中的称呼) =]) 这基本上是一些基本视图(标签、按钮、文本字段等)组合成一个可重用的子视图 class,所以我可以在我的 UIViewControllersActivity 中使用它在 Android.

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

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

在 iOS 中,可以这样简单地完成:

CustomView.h

@interface CustomView : UIView

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

@end

CustomView.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

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

如何在 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 应用程序,而不是尝试 bash Android 的做事方式。

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

这里只需要一些已经完成 iOS 和 Android 开发的其他人的指导。

编辑

我要找的好像是复合控件:http://developer.android.com/guide/topics/ui/custom-components.html

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

只需要解决这个Inflater业务。

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

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

<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 中创建自定义视图(为什么?)如果默认视图未提供我们代码中需要 use/implement 的某些功能,我们只需要自定义视图。 据我了解,您想使用自定义视图进行重用。这是一个很好的方法,但如果 android 提供了一些功能而不是你为什么要再次发明轮子,只需使用不同的布局,如果你想要一些额外的东西,只使用自定义视图。

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

所以它是这样的:

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

上面"extends RelativeLayout"里面的"RelativeLayout"当然可以换成"LinearLayout"或者其他布局

为这个问题的一般访问者添加一个简单的答案...

您不能像使用 iOS UIView. [一样将子视图添加到 Android View

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

myViewGroup.addView(myView);