Android Custom View Group Delegate Addview

Android custom view group delegate addView

Views inflated from a layout - like your example TextView - are not added to their parent ViewGroup with addView(View child), which is why overriding just that method didn't work for you. You want to override addView(View child, int index, ViewGroup.LayoutParams params), which all of the other addView() overloads end up calling.

In that method, check if the child being added is one of your two special FrameLayouts. If it is, let the super class handle the add. Otherwise, add the child to your container FrameLayout.

public class CustomFrameLayout extends FrameLayout {

private final FrameLayout topLayout;
private final FrameLayout containerLayout;

...

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

LayoutInflater.from(context).inflate(R.layout.custom, this, true);
topLayout = (FrameLayout) findViewById(R.id.frame_layout_top);
containerLayout = (FrameLayout) findViewById(R.id.frame_layout_child_container);
}

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
final int id = child.getId();
if (id == R.id.frame_layout_top || id == R.id.frame_layout_child_container) {
super.addView(child, index, params);
}
else {
containerLayout.addView(child, index, params);
}
}
}

How to use View Binding on custom views

Just inform the root, and whether you want to attach to it

init { // inflate binding and add as view
binding = ResultProfileBinding.inflate(LayoutInflater.from(context), this)
}

or

init { // inflate binding and add as view
binding = ResultProfileBinding.inflate(LayoutInflater.from(context), this, true)
}

which inflate method to use will depend on the root layout type in xml.

Android custom component doesn't display its children

When extending ViewGroup directly, you need to handle measuring and laying out the child Views yourself, or they never get sized or positioned correctly. Oftentimes, it's simpler to extend a ViewGroup subclass that already handles that stuff. In this case, you might as well extend RelativeLayout, since that's what's actually holding the internal Views, and the ViewGroup wrapping it is rather unnecessary.

The parent RelativeLayout in the internal layout XML is then redundant, and can be replaced with <merge> tags, which will cause the child Views to be added directly to your RelativeLayout subclass when inflated.

<merge xmlns:android="http://schemas.android.com/apk/res/android">

<RelativeLayout
android:id="@+id/field_set_content"
... />

<TextView
android:id="@+id/field_set_label"
... />

</merge>

We can further simplify things by setting up the internal structure in the constructor(s), and putting the child Views in the right place as they're added, rather than juggling all of that around in onFinishInflate(). For example:

public class FieldSet extends RelativeLayout {

final ViewGroup vg;

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

LayoutInflater.from(context).inflate(R.layout.field_set, this, true);
vg = (ViewGroup) findViewById(R.id.field_set_content);
}

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
final int id = child.getId();
if (id == R.id.field_set_content || id == R.id.field_set_label) {
super.addView(child, index, params);
}
else {
vg.addView(child, index, params);
}
}
}

Android custom view: Delegate attributes to controls inside of view

If you want to just use the android style attributes you could do something like the following.

In this example it should pick up the text attribute if you defined it in the xml when creating the InputView.

init {
attrs?.let {
val typedArray = context.obtainStyledAttributes(attrs, STYLE_ATTRIBUTES)
val text = typedArray.getText(0)
typedArray.recycle()
}
}

companion object {
private val STYLE_ATTRIBUTES = intArrayOf(
android.R.attr.text
)
}

Android ViewBinding with CustomView

ViewDataBinding.inflate doesn't generate of child view accessor inside custom view.

thus, you can't touch line1(TextView) via only use ViewDataBinding.

If you don't want using findViewById or kotlin synthetic, MyCustomView also needs to apply ViewDataBinding. try as below.

CustomView

class MyCustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private val binding =
CustomLayoutBinding.inflate(LayoutInflater.from(context), this, true)

val line1
get() = binding.line1

val line2
get() = binding.line2
}

MainActivity

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)

with(binding.customView) {
line1.text = "Hello"
line2.text = "World"
}
}

Can you set a view as a property of another view in an XML layout file?

Yes, you can.

You can create a Custom View by extending the GridView class and add some logic through attributes. This will not work as an attached property from XAML (Like Grid.Column or Grid.Row from XAML UWP) but you can do something like this:

<com.teste.widget.MarqueIVGridView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:my_header="@layout/my_header"
/>

Don't forget to add the namespace at the root of your layout:

xmlns:app="http://schemas.android.com/apk/res-auto"

Google has this sample:
HeaderGridView

It uses a different approach, if you copy this class you will just need to use this "HeaderGridView" and call addHeaderView method from it sending your header view inflated.

Feel free to ask any question and It will be a pleasure to answer.



Related Topics



Leave a reply



Submit