25

I have a set of views I want to always use together. An example of this may be something like:

<LinearLayout>
    <TextView />
    <EditView />
</LinearLayout>

The text view is a prompt, and the edit view is the answer. I would like to give this combination a name, and be able to use that name to pop it into xml. I'd love for it to be a custom view so I can put it nicely in a class and create various utility functions for it. Is there any way I can do that? I know I could subclass LinearLayout and create the children dynamically in the java code, but that loses me the ability to easily make changes via xml. Is there a better route?

And yes, I also have places I want to do this which are more involved than just prompts.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • shameless selfpromotion but might be relevant - http://stackoverflow.com/questions/11722340/edittext-on-demand-widget – Shark Oct 19 '12 at 16:23
  • Have you read Android's article on designing [Custom Components](http://developer.android.com/guide/topics/ui/custom-components.html)? – Sam Oct 19 '12 at 16:24
  • Yes i have Sam, and I've written 3 custom views in the past myself (a Camera View to show the camera, a UrlImageView to download an image from the web directly into an imageview, and a DragAndDrop image layout on top of AbsoluteLayout). But none of the things in that page show you how to define a widget with subviews in xml- they all do it in Java. I wish to avoid that. – Gabe Sechan Oct 19 '12 at 17:21

3 Answers3

56

This example is for a horizontal number picker widget, but it's the same concept.

First create the XML layout for your custom component

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

   <Button
       android:id="@+id/btn_minus"
       android:layout_width="50dp"
       android:layout_height="wrap_content"
       android:text="-" />

   <EditText
       android:id="@+id/edit_text"
       android:layout_width="75dp"
       android:layout_height="wrap_content"
       android:inputType="number"
       android:gravity="center"
       android:focusable="false"
       android:text="0" />

   <Button
       android:id="@+id/btn_plus"
       android:layout_width="50dp"
       android:layout_height="wrap_content"
       android:text="+" />
</LinearLayout>

Then create the java class

public class HorizontalNumberPicker extends LinearLayout {
    public HorizontalNumberPicker(Context context, AttributeSet attrs) {
         super(context, attrs);
         LayoutInflater inflater = LayoutInflater.from(context);
         inflater.inflate(R.layout.horizontal_number_picker, this);
     }
 }

Add whatever logic you need to that java class, then you can just include the custom component in your XML layout like so:

<com.example.HorizontalNumberPicker
     android:id ="@+id/horizontal_number_picker"
     android:layout_width ="wrap_content"
     android:layout_height ="wrap_content" />


Check out this link for more information: http://developer.android.com/guide/topics/ui/custom-components.html#compound

Tom K
  • 430
  • 5
  • 22
starkej2
  • 11,391
  • 6
  • 35
  • 41
  • I tried this, letting Android Studio convert it to Kotlin first. In the constructor I used 'this' for context, but I have no idea about the AttributeSet. Just learning Android (coming from an iOS developer background). – Scooter Apr 15 '18 at 01:35
  • 3
    If I understand this correctly, `HorizontalNumberPicker` has two `LinearLayouts`? It is itself one and it also inflates one in the constructor. Seems like one too many. – rozina Jul 04 '18 at 08:13
  • 1
    @rozina I think you can use the tag in place of LinearLayout in the custom component's layout file. That would eliminate the extra layout. – starkej2 Jul 05 '18 at 15:10
4

Use Compound controls for that purpose. There are a lot of samples and tutorials about it. Good luck )

cbrulak
  • 15,436
  • 20
  • 61
  • 101
rus1f1kat0R
  • 1,645
  • 1
  • 16
  • 22
1

Put that XML into a layout XML file and inflate the view using an inflater when you want to.

LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.some_id, parent, false);

Once you have the view you can write a utility class that accepts this view and performs operations on it. Retrieve the text and edit view by using findViewById() or storing the references to those other views using the ViewHolder pattern

Sam
  • 86,580
  • 20
  • 181
  • 179
Deepak Bala
  • 11,095
  • 2
  • 38
  • 49
  • Hmm. If I was to do this in the constructor of a class derived from a layout (say LinearLayout), I could reference that in the parent xml, and pass this in as the parent. If the top level xml of the child view was a merge tag it would eliminate the extra layout we'd otherwise need to shove in there. And I could use the ViewHolder pattern as well for efficiency, but with the holder itself a View so I can avoid doing all the ugly tags. I think this has potential, I'll test it later today when I'm back at home. – Gabe Sechan Oct 19 '12 at 17:53