9

I make a application with a button where in there a total of 4 texts and I want to align the first 2. One at the most left side of the bottom text and the other and the right side of the bottom text.

So from this:

Before

setText(item.title + " " + item.roomId + "\n" + item.teacher + " " + item.classes);

To this:

After (this is what I want)

setText(declare here a spannable);

I think I should work with Spannable, I've tried some things with Alignment.ALIGN_NORMAL and Alignment.ALIGN_OPPOSITE but I think is should first calculate the length of the bottom text and then do the alignment. (I've found a good example here but it's not working in my set-up).

I hope that someone can point me to a good direction.

Edit:

The reason that I can not (I think) use RelativeLayout or LinearLayout is that I'm extending button in a different class (ScheduleItemView.java):

/**
 * Custom view that represents a {@link ScheduleItem} instance, including its
 * title and time span that it occupies. Usually organized automatically by
 * {@link ScheduleItemsLayout} to match up against a {@link TimeRulerView}
 * instance.
 */
public class ScheduleItemView extends Button {

    private ScheduleItem mItem;

    public ScheduleItemView(Context context, ScheduleItem item) {
        super(context);

        mItem = item;

        setSingleLine(false);
        setText(item.title + " " + item.roomId + "\n" + item.teacher + " "
                + item.classes);

        // TODO: turn into color state list with layers?
        int textColor = Color.WHITE;
        int accentColor = item.accentColor;

        LayerDrawable buttonDrawable = (LayerDrawable) context.getResources()
                .getDrawable(R.drawable.btn_block);
        buttonDrawable.getDrawable(0).setColorFilter(accentColor,
                PorterDuff.Mode.SRC_ATOP);
        buttonDrawable.getDrawable(1).setAlpha(item.containsStarred ? 255 : 0);

        setTextColor(textColor);
        setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources()
                .getDimensionPixelSize(R.dimen.text_size_small));

        setGravity(Gravity.CENTER | Gravity.BOTTOM);

        setBackgroundDrawable(buttonDrawable);
    }

    public ScheduleItem getScheduleItem() {
        return mItem;
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
                MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
                getMeasuredHeight(), MeasureSpec.EXACTLY));
        // layout(getLeft(), getTop(), getRight(), getBottom());
        setGravity(Gravity.CENTER);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getRight() - getLeft(), getBottom() - getTop());
    }
}

I've tried to do this in protected void onLayout (ScheduleItemsLayout.java):

child.setLayoutParams(new LayoutParams(
        LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

But that's not working. I'm not sure if I should use new RelativeLayout(this).

Is it better to use Spannable in this case?

The source of the project can be downloaded here (which you can import in Eclipse)

Community
  • 1
  • 1
user2784435
  • 409
  • 5
  • 18
  • 2
    use relative layout instead and take separate text views and do the needful placement – Suhail Mehta Nov 25 '13 at 12:17
  • @SuhailMehta I'm sorry I've tried relative layout but I can't use it because I'm extending `Button` in a different class. In that class I've tried to setContentView but then it says it's undefined. I think working with Spannable is the only solution. – user2784435 Nov 25 '13 at 12:32

3 Answers3

4

Well, if you can't make the move from Button to a ViewGroup, here's what you can do in your extended Button class:

Remove:

setText(item.title + " " + item.roomId + "\n" + item.teacher + " "
            + item.classes);

Add the following to ScheduleItemView's constructor:

float[] fl1 = new float[item.title.length()];
getPaint().getTextWidths(item.title, fl1);

float[] fl2 = new float[item.roomId.length()];
getPaint().getTextWidths(item.roomId, fl2);

float[] fl3 = new float[item.teacher.length() + item.classes.length() + 1];
getPaint().getTextWidths(item.teacher + " " + item.classes, fl3);

float differenceInWidth = sumUpTheArray(fl3) 
                          - sumUpTheArray(fl1) 
                          - sumUpTheArray(fl2);

float fSpaceArray[] = new float[1];

getPaint().getTextWidths(" ", fSpaceArray);

int numOfSpaces = (int) (differenceInWidth / fSpaceArray[0]);

char[] spaceCharArr = new char[numOfSpaces];

Arrays.fill(spaceCharArr, ' ');

setText(item.title + String.valueOf(spaceCharArr) + item.roomId + "\n" 
                            + item.teacher + " " + item.classes);

Add this helper method which sums up a float array:

public float sumUpTheArray(float[] arr) {
    float sum = 0f;

    for (int i = 0; i < arr.length; i++) {
        sum += arr[i];
    }

    return sum;
}

Not sure why you're overriding the onDraw(Canvas) and onMeasure(int, int) methods.

Needless to say, this is one convoluted code snippet. What's happening is that we're measuring how much space we must put between title and roomId to get the desired alignment. The code is straight-forward (though cringe-worthy), so no comments have been included.

On the other hand, you could extend RelativeLayout:

public class ScheduleItemAlternateView extends RelativeLayout {

    private ScheduleItem mItem;  

    public ScheduleItemAlternateView(Context context, ScheduleItem item) {
        super(context);

        mItem = item;

        int textColor = Color.WHITE;
        int accentColor = item.accentColor;

        LayerDrawable buttonDrawable = (LayerDrawable) context.getResources()
            .getDrawable(R.drawable.btn_block);
        buttonDrawable.getDrawable(0).setColorFilter(accentColor,
            PorterDuff.Mode.SRC_ATOP);
        buttonDrawable.getDrawable(1).setAlpha(item.containsStarred ? 255 : 0);

        // Three TextViews to hold the `title`, `roomId`
        // and `teacher&room` independently
        TextView tvTitle = new TextView(context);
        TextView tvRoomId = new TextView(context);
        TextView tvTeacherAndClasses = new TextView(context);

        // Example ids
        tvTitle.setId(100);
        tvRoomId.setId(101);
        tvTeacherAndClasses.setId(102);

        tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources()
            .getDimensionPixelSize(R.dimen.text_size_small));
        tvRoomId.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources()
            .getDimensionPixelSize(R.dimen.text_size_small));
        tvTeacherAndClasses.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources()
            .getDimensionPixelSize(R.dimen.text_size_small));

        tvTitle.setPadding(30, 20, 30, 0);
        tvRoomId.setPadding(30, 20, 30, 0);
        tvTeacherAndClasses.setPadding(30, 5, 30, 20);

        tvTitle.setTextColor(textColor);
        tvRoomId.setTextColor(textColor);
        tvTeacherAndClasses.setTextColor(textColor);

        // Set text
        tvTitle.setText(item.title);        
        tvRoomId.setText(item.roomId);      
        tvTeacherAndClasses.setText(item.teacher + " " + item.classes);

        // LayoutParms
        RelativeLayout.LayoutParams paramsTitle = 
                                 new RelativeLayout.LayoutParams(
                                     RelativeLayout.LayoutParams.WRAP_CONTENT,
                                     RelativeLayout.LayoutParams.WRAP_CONTENT);

        paramsTitle.addRule(RelativeLayout.ALIGN_LEFT, 
                                                  tvTeacherAndClasses.getId());

        RelativeLayout.LayoutParams paramsRoomId = 
                                 new RelativeLayout.LayoutParams(
                                     RelativeLayout.LayoutParams.WRAP_CONTENT,
                                     RelativeLayout.LayoutParams.WRAP_CONTENT);

        paramsRoomId.addRule(RelativeLayout.ALIGN_RIGHT, 
                                                  tvTeacherAndClasses.getId());

        RelativeLayout.LayoutParams paramsTeacherAndClasses = 
                                 new RelativeLayout.LayoutParams(
                                     RelativeLayout.LayoutParams.WRAP_CONTENT,
                                     RelativeLayout.LayoutParams.WRAP_CONTENT);

        paramsTeacherAndClasses.addRule(RelativeLayout.CENTER_HORIZONTAL);
        paramsTeacherAndClasses.addRule(RelativeLayout.BELOW, tvTitle.getId());

        // Add Views to this RelativeLayout
        addView(tvTitle, paramsTitle);
        addView(tvRoomId, paramsRoomId);
        addView(tvTeacherAndClasses, paramsTeacherAndClasses);

        // Set the background as LayerDrawable
        setBackgroundDrawable(buttonDrawable);          
    }
}

Here, we create three TextViews, and set their LayoutParams to get proper alignment.

Output is the same for both, though I would recommend the second approach:

enter image description here

Vikram
  • 49,828
  • 11
  • 86
  • 118
  • I've tried your second code, but only the `LayerDrawable` is displaying the text is not displaying. I'm using the same source as [devoxx](http://goo.gl/GWyslV). My `ScheduleItemView.java` can be found [here](http://pastebin.com/2Qn5Vzzv). The view is added in [`ScheduleItemsLayout.java`](http://goo.gl/PgAn66) with `addView(scheduleItemView, 1);` (maybe this is wrong). How is your view added? I've already tried to set the Gravity and to disable padding but that doesn't work. (The item.title, item.roomId, etc. is not null). – user2784435 Dec 07 '13 at 11:58
  • @user2784435 Let's talk: [here](http://chat.stackoverflow.com/rooms/42706/align-2-texts-1-normal-1-opposite). – Vikram Dec 07 '13 at 15:24
  • The problem is still not fixed, but I've managed to reproduce the problem. I've cleaned out my whole code and only the important parts I've kept. The source of the project can be downloaded [here](http://we.tl/5PnmaZKXvL) (which you can import in Eclipse). – user2784435 Jan 28 '14 at 12:49
  • @user2784435 Extremely sorry for replying so late. First it was the holiday season, and now its school that's keeping me busy. I tried the link in your comment - seems like its dead now. Could you upload the project again? I'll take a look at it as soon as I can. – Vikram Feb 01 '14 at 08:52
  • I've reupload the project [here](http://we.tl/othKJd3Lw0). I hope that you can help me with this! :) – user2784435 Feb 01 '14 at 10:04
  • 2
    @user2784435 Please take a look at [ScheduleItemView.java](http://ge.tt/3XA6d7I1/v/0?c) and [sch_item.xml](http://ge.tt/77hId7I1/v/0?c). Copy code from first link to `ScheduleItemView` in your project. Add `sch_item.xml` to your `res/layout` folder. Open up `ScheduleItemsLayout` from your project and change its `onMeasure(int, int)` to [link](http://pastebin.com/MnCggtuz). See if this produces the desired result. – Vikram Feb 02 '14 at 22:39
  • Woaah thanks! I'll accept your answer. Only 2 things when the text of `tvTeacherAndClasses` is longer then the `LayerDrawable` it will not put some text on a `new line`. [See this](http://i.imgur.com/btdgBRH.png) ([this](http://i.imgur.com/taKKzUT.png) is how I want it, [or even better](http://i.imgur.com/iAgUAME.png) (show only the first class)). And when `tvTitle` and `tvRoomId` is longer then `tvTeacherAndClasses` it will also [fail](http://i.imgur.com/SlyTmvs.png). The project can be downloaded [here](http://we.tl/fuUdG1T3Sx). Is it maybe better to use `TableLayout`? – user2784435 Feb 03 '14 at 10:59
  • @user2784435 Thanks for accepting the answer. The 2 issues you mention should be manageable. I'll look into them as soon as I can. The problem is that I'm really pressed for time this semester. An advanced data structure course is killing me. I'll get back to you in a day or two. – Vikram Feb 03 '14 at 18:23
  • Take your time. In the meantime, I tried some things with `TableLayout` but I think this is not a replacement for `RelativeLayout`. – user2784435 Feb 09 '14 at 09:11
  • Also tried somethings with [AutoResizeTextView](http://stackoverflow.com/a/5535672/1480019) also that doesn't work. I hope that you help me with this (there's no hurry). – user2784435 May 02 '14 at 10:53
2

One of the easiest ways I have found to achieve this kind of behavior is to take advantage of the android layout_weight property like so:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="30dp"
    android:background="@color/Orange"
    android:orientation="vertical"
    android:padding="20dp" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/top_left_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="SCHK" />

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" >
        </LinearLayout>

        <TextView
            android:id="@+id/top_right_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="G102" />
    </LinearLayout>

    <TextView
        android:id="@+id/bottom_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="VIM M4O3WI, M404WI, M402SJ" />

</LinearLayout>

In this way the LinearLayout with a weight of 1 will take up the space needed to properly separate the two strings on the top line as desired.

Also why are you "extending Button in a different class"? If you are doing this purely to get the rounded corner/orange background that is displayed in your question, you can get this same effect by setting the background of the top-level linear layout to that of the button's style.

Cumulo Nimbus
  • 6,542
  • 7
  • 43
  • 61
  • See my edit. The rounded corner/orange background is set through `LayerDrawable`. Could you maybe explain how to implement your `LinearLayout` in my code? – user2784435 Nov 28 '13 at 19:40
1

Use 3 TextViews inside RelativeLayout.

for title

    android:id="@+id/title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_centerVertical="true"

for roomId

    android:id="@+id/roomId"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true"

for teacher

    android:id="@+id/teacher"
    android:layout_below="@+id/title"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
VenomVendor
  • 14,120
  • 11
  • 63
  • 89
  • I'm sorry I can't use `RelativeLayout` because I'm extending `Button` in a different class (`setContentView` or `inflating` a layout doesn't work in that class). I think working with `Spannable` is the only solution. – user2784435 Nov 26 '13 at 13:38