创建iOS子视图

时间:2015-05-10 10:15:58

标签: android ios android-custom-view android-viewgroup

我正在尝试以编程方式(不使用XML文件)在Android中创建自定义子视图(这就是我在iOS中所称的),这基本上是一些基本视图(标签,按钮,文本字段等)放在一个可重复使用的子视图类中,这样我就可以在Android UIViewControllersActivity中使用它。

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

自定义视图,ViewGroups,布局,窗口小部件,组件,以及您想要调用它的任何内容。

在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应用程序而不是试图压制Android的做事方式。

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

只需从其他已完成iOS和Android开发的人那里获得一些指导。

修改

似乎我正在寻找的是一个复合控制:http://developer.android.com/guide/topics/ui/custom-components.html

我会继续挖掘并希望达到我追求的结果:D

只需要解决这个Inflater业务。

2 个答案:

答案 0 :(得分: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); 
    }
}

现在,我可以在我的任何活动中使用此自定义视图:

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

“extendLayout”中的上述“RelativeLayout”当然可以替换为“LinearLayout”或其他布局。

答案 1 :(得分:1)

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

您无法像使用iOS EditingDomain一样向Android View添加子视图。

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

RelativeLayout