Showing posts with label Custom Design. Show all posts
Showing posts with label Custom Design. Show all posts

Tuesday, July 5, 2011

Custom Tabs that Scroll Horizontally

So I designed horizontally scrolling tabs that use separate activities to fill the tab content. TI rate this a 1/5 on the fun scale: there are so many different files you have to create and different selection states you have to consider. (Click here for my update to this tutorial: Changing Tab with Horizontal Swipe Gesture)

Faded edges indicate additional content
tabhostlayout.xml - Pretty basic stuff here, if you followed the tab widget tutorial on the android dev site.[1] The only thing to note here is that I surrounded the tab widget with Horizontal Scroll View element to achieve a horizontal scroll for the tabs. (Note: Sorry for the formatting here. I've tried to fix it only to make it much much worse. Blame blogger?)
<TabHost
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@android:id/tabhost"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent">
<LinearLayout
          android:orientation="vertical"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent">
<HorizontalScrollView
               android:layout_width="fill_parent"
               android:layout_height="wrap_content">
                <TabWidget
                    android:id="@android:id/tabs"
                   android:layout_width="fill_parent"
                    android:layout_height="wrap_content" />
</HorizontalScrollView>
<FrameLayout
               android:id="@android:id/tabcontent"
               android:layout_width="fill_parent"
               android:layout_height="fill_parent"
               android:padding="5dp" />
</LinearLayout>
</TabHost>

TabHostActivity.java - This activity must extend TabActivity. You will need to define the following variables and also set the divider drawable BEFORE setting up tabs

TabHost mTabHost = (TabHost) findViewById(android.R.id.tabhost);
mTabHost.getTabWidget().setDividerDrawable(R.drawable.tab_divider);
Intent intent; // Reusable Intent for each tab

For each tab you would like to add, use the following code in onCreate:
intent = new Intent().setClass(TabHostActivity.this, PageOne.class);
setupTab(new TextView(this), "Tab One", intent);

After onCreate, you need to add two new classes. "setupTab" creates the tabs and tab content. "createTabView" handles all tab events, like setting the views for selection events and stuff like that.
private void setupTab(final View view, final String tag, final Intent myIntent) {
View tabview = createTabView(mTabHost.getContext(), tag);
TabSpec setContent = mTabHost.newTabSpec(tag).setIndicator(tabview).setContent(myIntent);
mTabHost.addTab(setContent);
}
private static View createTabView(final Context context, final String text) {
View view = LayoutInflater.from(context).inflate(R.layout.tabs_bg, null);
TextView tv = (TextView) view.findViewById(R.id.tabsText);
tv.setText(text);
return view;
}

tabs_bg.xml - In the code above, "createTabView" calls a layout called tabs_bg. Create this file in the layout folder. This layout creates the layout for each tab element. I have only added a single textview but an image view could also be added (but will need some adjustments to the java code not shown above). This is where some of the layout personalization comes in. Text size and style, and padding elements. Note the background and text color call drawable files that handle the color changes for selection events.
<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/tabsLayout"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:background="@drawable/tab_bg_selector"
     android:padding="3dip"
     android:gravity="center"
     android:orientation="vertical">
<TextView
          android:layout_width="wrap_content"
          android:textColor="@drawable/tab_text_selector"
          android:gravity="center"
          android:textSize="12dip"
          android:layout_height="wrap_content"
          android:layout_gravity="center"
          android:id="@+id/tabsText"
          android:text="Title"
          android:padding="10px">
</TextView>
</LinearLayout>

tab_bg_selector.xml - both bg and text selector drawable files require the same four states to be defined: active, pressed, selected (using arrows), and inactive. I don't really know why the text and bg have to be separate files, but when I tried to combine them, it wasn't displaying properly. This code resembles custom button selection.
<selector
     xmlns:android="http://schemas.android.com/apk/res/android">
     <!--  Active tab -->
   
<item
          android:state_selected="true"
          android:state_focused="false"
          android:state_pressed="false">
          <shape
               android:shape="rectangle">
               <gradient
                    android:startColor="#A8A8A8"
                    android:centerColor="#7F7F7F"
                    android:endColor="#696969"
                    android:angle="-90" />
          </shape>
     </item>
   
<!--  Inactive tab -->
   
<item
          android:state_selected="false"
          android:state_focused="false"
          android:state_pressed="false">
          <shape
               android:shape="rectangle">
               <gradient
                    android:startColor="#5C5C5C"
                    android:centerColor="#424242"
                    android:endColor="#222222"
                    android:angle="-90" />
          </shape>
     </item>
<!--  Pressed tab -->
<item
          android:state_pressed="true"
          android:drawable="@android:color/transparent" />
<!--  Selected tab (using d-pad) -->
 <item
          android:state_selected="true"
          android:state_focused="true"
          android:state_pressed="false"
          android:drawable="@android:color/transparent" />
</selector>

tab_text_selector.xml
<selector
     xmlns:android="http://schemas.android.com/apk/res/android">
     <item
          android:state_selected="true"
          android:color="@android:color/white" />
<item
          android:state_focused="true"
          android:color="@android:color/white" />
<item
          android:state_pressed="true"
          android:color="@android:color/white" />
   
<item
          android:color="#f8f8f8" />
</selector>

References:
1) http://developer.android.com/resources/tutorials/views/hello-tabwidget.html
2)http://joshclemm.com/blog/?p=136
For additional help, click here to download a zip containing all the files needed to create the above project.

Wednesday, June 8, 2011

So you need to change the text size of simple_list_item_single_choice...

I feel you.

I tried for a month straight to call the checkedtextview in the android standard layout and resize the text. It never let me. I felt like a idiot. And now that I've figured it out, I know that it is more than just a feeling.

And so I'm posting it here, in the event that I actually forget how to do this later on. Because.. you know... I'm an idiot.

I went and copied the xml from the api demo and pasted it into a new .xml file(step 1). I accessed the text size and adjusted it thusly (step 2). Then I changed the java code to replace the simple_list_item_single_choice with my own custom row layout (step 3). Facepalmed for about two hours (step 4).

my_listitem_singlechoice.xml (i just chaged "textAppearance" attribute but I'm sure you can do other stuff to it too) :


<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:textAppearance="?android:attr/textAppearanceSmall"
    android:gravity="center_vertical"
    android:checkMark="?android:attr/listChoiceIndicatorSingle"
    android:paddingLeft="6dip"
    android:paddingRight="6dip"
/>



my_java_with_singlechoice.java (this should look somewhat familiar) :


        ListView lv = this.getListView();
MyListAdapter = new ArrayAdapter<String>(this, R.layout.my_listitem_singlechoice, myArrayList);
lv.setAdapter(MyListAdapter );
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

Tuesday, February 1, 2011

Button with Image and Text

This all goes down in the XML file with the button in question. This can also be accomplished dynamically (not shown.) Here is the excerpt from the xml file defining the button. The image should be located in the drawable folder. The image is called from android:drawable. Use drawableLeft, drawableRight, drawableTop, drawableBottom depending on how you want to place the image in relation to the text. Adjust the padding to suit your aesthetic.



<Button
    android:layout_width="wrap_content"
    android:text="Sample Text"
    android:id="@+id/Button01"
    android:layout_height="wrap_content"
    android:drawableLeft="@drawable/search"
    android:drawablePadding="0px"
    android:padding="0px">
</Button>

Friday, January 28, 2011

Custom Spinner Row Layout & Custom Spinner Adapter

The custom spinner adapter is similar to the custom list row & adapter, because we can use LayoutInflater.

(*edit: This code may not run as-is. Therefore, I have included project files associated with this post available for download at the end of this post.)

1) The spinner layout. This is how each row will look when the spinner is selected. In my example there are three text views as three rows.

<LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent" android:orientation="vertical">
      <TextView
          android:layout_height="wrap_content"
          android:id="@+id/TextView01"
          android:textSize="12px"
          android:layout_width="match_parent"
          android:textColor="#000000">
      </TextView>
      <TextView
          android:layout_height="wrap_content"
          android:id="@+id/TextView02"
          android:layout_width="match_parent"
          android:textSize="12px" android:textColor="#000000"></TextView>
      <TextView
          android:layout_height="wrap_content"
          android:id="@+id/TextView03"
          android:layout_width="match_parent"
          android:textSize="12px"
          android:textColor="#000000">
      </TextView>
</LinearLayout>




2) The custom adapter

// CUSTOM SPINNER ADAPTER
public class MyCustomSpinnerAdapter extends ArrayAdapter<String> {

public MyCustomSpinnerAdapter(Context context, int textViewResourceId,
String[] objects) {
super(context, textViewResourceId, objects);
// TODO Auto-generated constructor stub
}

@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return getCustomView(position, convertView, parent);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return getCustomView(position, convertView, parent);
}

public View getCustomView(int position, View convertView,ViewGroup parent) {
// TODO Auto-generated method stub
// return super.getView(position, convertView, parent);

LayoutInflater inflater = getLayoutInflater();

ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.customspinneritem, null);
holder = new ViewHolder();
holder.txt01 = (TextView) convertView.findViewById(R.id.TextView01);
holder.txt02 = (TextView) convertView.findViewById(R.id.TextView02);
holder.txt03 = (TextView) convertView.findViewById(R.id.TextView03);

convertView.setTag(holder);

} else {

holder = (ViewHolder) convertView.getTag();
}

holder.txt01.setText("Route")
holder.txt02.setText("Strength");
holder.txt03.setText("Form");

return convertView;
}

class ViewHolder {
TextView txt01;
TextView txt02;
TextView txt03;
}

} // end custom adapter


3) Calling the spinner & setting the adapter

Spinner spinner = (Spinner) findViewById(R.id.spinner1);
spinner.setPrompt("Select One");
spinner.setAdapter(new MyCustomSpinnerAdapter(this, 0, items));
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
// your code here
}

@Override
>public void onNothingSelected(AdapterView<?> parentView) {
// your code here
}
});


4) Setting string items

String[] items = { "Route 1, Strength 1, Form 1", "Route 2, Strength 2, Form 2", "Route 3, Strength 3, Form 3" };

Downloads:
main.xml
customspinneritem.xml
CustomSpinnerActivity.java (select "qui")

Resources:
My previous Spinner post
Android Dev Tutorial - Hello Spinner
Custom List Row Layout w/ Adapter

Friday, January 14, 2011

Seriously, this is how I made a multi-column list item populated by strings!!

Untitled Document 1) The item layout:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/LinearLayout01"
android:layout_alignParentLeft="true"
android:layout_height="fill_parent"
android:orientation="vertical"
android:layout_width="fill_parent">
<TextView
android:layout_height="wrap_content"
android:id="@+id/TextView_Title"
android:text="Title"
android:textSize="15px"
android:layout_width="fill_parent"></TextView>

<LinearLayout
android:id="@+id/LinearLayout02"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_height="wrap_content"
android:id="@+id/TextView_Item1"
android:text="Item1"
android:layout_width="wrap_content"
android:textSize="10px" android:layout_weight="1"/>
    <TextView
android:layout_height="wrap_content"
android:id="@+id/TextView_Item2"
android:text="Item2"
android:layout_width="wrap_content"
android:textSize="10px" android:layout_weight="1"/>
<TextView
android:layout_height="wrap_content"
android:id="@+id/TextView_Item3"
android:text="Item3"
android:layout_width="wrap_content"
android:textSize="10px" android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>




2) Build the custom adapter:

// CUSTOM TWO ROW LIST ADAPTER
static class EfficientAdapter extends BaseAdapter {
private LayoutInflater mInflater;

public EfficientAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}

public int getCount() {
return title.size();
}

public Object getItem(int position) {
return position;
}

public long getItemId(int position) {
return position;
}

public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listitem_addrx, null);
holder = new ViewHolder();
holder.txtTitle = (TextView) convertView.findViewById(R.id.TextView_Title);
holder.txtItem1 = (TextView) convertView.findViewById(R.id.TextView_Item1);
holder.txtItem2 = (TextView) convertView.findViewById(R.id.TextView_Item2);
holder.txtItem3 = (TextView) convertView.findViewById(R.id.TextView_Item3);

convertView.setTag(holder);
holder = (ViewHolder) convertView.getTag();
}

holder.txtTitle.setText(title.get(position));
holder.txtItem1.setText(one.get(position));
holder.txtItem2.setText(two.get(position));
holder.txtItem3.setText(three.get(position));

return convertView;
}

static class ViewHolder {
TextView txtTitle;
TextView txtItem1;
TextView txtItem2;
TextView txtItem3;
}
} // end custom adapter


3) Call the following items:

static ListView lv;
public static ListAdapter adapter;
String selected;
static final ArrayList<String> title = new ArrayList<String>();
static final ArrayList<String> one = new ArrayList<String>();
static final ArrayList<String> two = new ArrayList<String>();
static final ArrayList<String> three = new ArrayList<String>();


4) Set the list adapter:

lv = (ListView) getListView();
        lv.setAdapter(new EfficientAdapter(this));


5) Add entries to list:

Either define a string array when you call the variables in step 3 or add items dynamically:


title.add(selected);
route.add(selected);
strength.add(selected);
form.add(selected);

lv.setAdapter(new EfficientAdapter(getBaseContext()));



References (& many, many thanks to):

Custom list row & adapter: http://www.androidpeople.com/android-custom-dynamic-listview/


Custom spinner row & adapter: http://android-er.blogspot.com/2010/12/custom-arrayadapter-for-spinner-with.html


Update with my version later...

Custom Button Using XML

1. Create a custombuttonlayout.xml file in the drawable folder


Selector: this element surrounds the entire xml. It defines this drawable as a selecter (e.g. buttons, list rows, or anything that can be "selected")


Items: There are three buttons states that must be defined: pressed, focused, normal. However, there are a total of seven states that can be defined(pressed, focused, selected, checkable, checked, enabled, and window_focused).These are defined as three separate "items". Due to the nature of the way java programs are read (basically from the top down) you need to define items in this specific order, with pressed first, focused next, and normal last to get the buttons to display correctly.


Gradient: Android buttons typically have a highlight/lowlight gradient, so you should define two colors, and the angle of the "shadow".


Stroke: I think this defines the outline style of the button? Not too sure...


Corners: creates a rounded button corner. The default android radius value is "5dp".


Padding: This defines the blank space between the button text and the button borders.


custombuttonlayout.xml



<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:state_pressed="true">
    <shape>
      <gradient
        android:startColor="#FFFF40"
        android:endColor="#FFFFC0"
        android:angle="270" />
      <stroke
        android:width="1dp"
        android:color="#000000" />
      <corners
        android:radius="5dp" />
      <padding
        android:left="10dp"
        android:top="15dp"
        android:right="10dp"
        android:bottom="15dp" />
    </shape>
  </item>
  <item
    android:state_focused="true">
    <shape>
      <gradient
        android:endColor="#8080FF"
        android:startColor="#C0C0C0"
        android:angle="270" />
      <stroke
        android:width="1dp"
        android:color="#4A494A" />
      <corners
        android:radius="5dp" />
      <padding
        android:left="10dp"
        android:top="15dp"
        android:right="10dp"
        android:bottom="15dp" />
    </shape>
  </item>
  <item>
    <shape>
      <gradient
        android:endColor="#8080FF"
        android:startColor="#C0C0C0"
        android:angle="270" />
      <stroke
        android:width="1dp"
        android:color="#000000" />
      <corners
        android:radius="5dp" />
      <padding
        android:left="10dp"
        android:top="15dp"
        android:right="10dp"
        android:bottom="15dp" />
    </shape>
  </item>
</selector>




2. In the main.xml layout when you insert a button that you want to apply the layout to:


Define the button background source as the drawable/custombuttonlayout.xml from above.


excerpt from main.xml:



<Button
          android:layout_width="wrap_content"
          android:text="Sample Text"
          android:id="@+id/Button01"
          android:layout_height="wrap_content"
          android:background="@drawable/custombuttonlayout"></Button>






Resources:


1) http://developer.android.com/guide/topics/resources/drawable-resource.html#StateList


2) http://developer.android.com/guide/topics/resources/color-list-resource.html


3) http://developer.android.com/reference/android/widget/Button.html


4) http://androiddrawableexplorer.appspot.com/


5) http://developer.android.com/guide/topics/resources/drawable-resource.html#Shape

Thursday, October 7, 2010

Easy ListView with Anchored Header/Footer and "Custom" Row Layout.

This is the basic listview that has been working great for me.

Disclaimer: While the list item view for this is "custom" it does not allow you to provide more than one view per list item. You can customize that one item (background, font, size, color, etc.) To add more views to a list item please see my entry on custom list adapters.

First is the page layout. I only included a simple textview header, but it could easily be replaced with buttons or checkboxes or anything. The surrounding linear layout but be set to "fill_parent" height and width and also have a vertical orientation. The listview width should be set to "fill_parent" so that the whole list row is clickable and not just the text. layout_weight should be set to 1 which will cause the listview to expand to the height of available space in the window, pushing the header or footer to be docked at the top or bottom.



pageone.xml

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

  android:layout_height="fill_parent"
  android:layout_width="fill_parent"
  android:orientation="vertical" >

<TextView android:layout_height="wrap_content"
  android:layout_width="fill_parent"
  android:text="This is the Header"
  android:id="@+id/Header" >
</TextView>

<ListView
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_weight="1"
  android:textFilterEnabled="true"
  android:id="@android:id/list" >
</ListView>
</LinearLayout>


The row layout is important if you want to customize the look of your list (like text size, color, etc). Otherwise, android has some built in layouts that can be used. In my example, I am just using a single textview (easily populated by string values using simpleadapter) with a small font. Add more textviews in a horizontal linear layout for columns, or vertical for two rows of text. Use relative layout to add photos and for even more customization. Remember the id of the textviews that will be populated by simple adapter later on. (EDIT: If you want to use additional text views, image views check boxes or whatever, you need to use a custom adapter!)

list_item.xml

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:padding="10dp"
  android:textSize="16sp"
  android:id="@+id/label">
</TextView>


In the java file, include a String of the items you want to display before the onCreate. Set the list Adapter in onCreate. Set up list item click event handler after onCreate. Right now, it is set up to display a toast message of the list item selected.

PageOne.java

public class StepTherapy extends ListActivity {

String[] items={"lorem", "ipsum", "dolor", "sit", "amet",
"consectetuer", "adipiscing", "elit", "morbi", "vel",
"ligula", "vitae", "arcu", "aliquet", "mollis",
"etiam", "vel", "erat", "placerat", "ante",
"porttitor", "sodales", "pellentesque", "augue",
"purus"};
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.steptherapy);

ListView lv = (ListView)getListView();
        lv.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item, R.id.label, items));
    public void onListItemClick(ListView parent, View v, int position, long id) {
            Toast.makeText(getApplicationContext(), ((TextView) v).getText(), Toast.LENGTH_SHORT).show();
}
}