Create tag cloud

I need to create a basic tag cloud ... My goal is to add multiple TextViews (tags) that will automatically fit onto a new line if the number exceeds the width of the device. (Similar to Instagram).

Right now, when the width of the layout containing ( TextView ) exceeds ( device width ) the TextView (s) at the end wraps itself. TextView is last distorted

I tried using RelativeLayout, but I can't figure out the logic. I've looked over this one , but if there is a better or just a cleaner solution.

Thanks for your precious time.

There is no need for the result to look like Instagram. what i want to achieve

+3


source to share


2 answers


A cleaner solution is to create your own ViewGroup class. Find a sample below.

For a full descriptive explanation, visit How to Create a Custom Layout in Android by Extending the ViewGroup Class .

public class TagLayout extends ViewGroup {

    public TagLayout(Context context) {
        super(context);
    }

    public TagLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        final int count = getChildCount();
        int curWidth, curHeight, curLeft, curTop, maxHeight;

        //get the available size of child view
        final int childLeft = this.getPaddingLeft();
        final int childTop = this.getPaddingTop();

        final int childRight = this.getMeasuredWidth() - this.getPaddingRight();
        final int childBottom = this.getMeasuredHeight() - this.getPaddingBottom();

        final int childWidth = childRight - childLeft;
        final int childHeight = childBottom - childTop;

        maxHeight = 0;
        curLeft = childLeft;
        curTop = childTop;
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE)
                return;

            //Get the maximum size of the child
            child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.AT_MOST));
            curWidth = child.getMeasuredWidth();
            curHeight = child.getMeasuredHeight();
            //wrap is reach to the end
            if (curLeft + curWidth >= childRight) {
                curLeft = childLeft;
                curTop += maxHeight;
                maxHeight = 0;
            }
            //do the layout
            child.layout(curLeft, curTop, curLeft + curWidth, curTop + curHeight);
            //store the max height
            if (maxHeight < curHeight)
                maxHeight = curHeight;
            curLeft += curWidth;
        }
    }
}

      

To use TagLayout, you can add it to your activity / fragment description declaration. main_activity.xml

<com.javatechig.taglayout.TagLayout
    android:id="@+id/tagLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

      

We can now have a custom view that allows us to set a certain level of customization for each tag layout.



tag_layout.xml

<TextView
    android:id="@+id/tagTextView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:background="#a000"
    android:padding="10dp"
    android:textColor="#fff" />

      

MainActivity.java Finally, from the activity class, you can add tag elements as follows.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TagLayout tagLayout = (TagLayout) findViewById(R.id.tagLayout);
        LayoutInflater layoutInflater = getLayoutInflater();
        String tag;
        for (int i = 0; i <= 20; i++) {
            tag = "#tag" + i;
            View tagView = layoutInflater.inflate(R.layout.tag_layout, null, false);

            TextView tagTextView = (TextView) tagView.findViewById(R.id.tagTextView);
            tagTextView.setText(tag);
            tagLayout.addView(tagView);
        }
    }
}

      

Result

enter image description here

+9


source


I believe the solution above always takes into account the full width of the screen, so if a developer sets a parameter android:layout_width

, for example, it will not respect its value or even the parent field and additional values.

I fixed it like this:

private int mScreenWidth = 0;
private int mAvailableWidth = -1;

public TagLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

private void init(Context context) {

    Display display = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    Point deviceSize = new Point();
    display.getSize(deviceSize);

    mScreenWidth = deviceSize.x;
}

private void calculateAvailableWidth() {

    if(getLayoutParams() != null && getLayoutParams().width > 0) {
        mAvailableWidth = getLayoutParams().width;
        return;
    }

    mAvailableWidth = mScreenWidth;

    ViewGroup parent = this;

    while(parent != null) {

        mAvailableWidth -= parent.getPaddingLeft() + parent.getPaddingRight();

        if(parent.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
            ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)parent.getLayoutParams();
            mAvailableWidth -= layoutParams.leftMargin + layoutParams.rightMargin;
        }

        if(parent.getParent() instanceof ViewGroup)
            parent = (ViewGroup)parent.getParent();
        else
            parent = null;
    }
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
    int currentRowWidth = 0;
    int currentRowHeight = 0;
    int maxItemWidth = 0;
    int maxWidth = 0;
    int maxHeight = 0;

    if(mAvailableWidth == -1)
        calculateAvailableWidth();

    for(int i = 0; i < count; i++) {
        View child = getChildAt(i);

        if(child.getVisibility() == GONE)
            continue;

        try {
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
        }
        catch(Exception e) {
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }

        int childWidth = child.getMeasuredWidth() + child.getPaddingRight() + child.getPaddingLeft();
        int childHeight = child.getMeasuredHeight() + child.getPaddingTop() + child.getPaddingBottom();

        maxItemWidth = Math.max(maxItemWidth, childWidth);

        if(currentRowWidth + childWidth < mAvailableWidth) {
            currentRowWidth += childWidth;
            maxWidth = Math.max(maxWidth, currentRowWidth);
            currentRowHeight = Math.max(currentRowHeight, childHeight);
        }
        else {
            currentRowWidth = childWidth;
            maxHeight += currentRowHeight;
        }
    }

    if(getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
        mAvailableWidth = maxItemWidth;
        maxWidth = maxItemWidth;
    }

    maxHeight += currentRowHeight + getPaddingTop() + getPaddingBottom();
    setMeasuredDimension(maxWidth, maxHeight);
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    int count = getChildCount();

    int currentLeft = getPaddingLeft();
    int currentTop = getPaddingTop();
    int currentRight;
    int currentBottom;

    int parentWidth = this.getPaddingRight() - this.getPaddingLeft() + right;

    for(int i = 0; i < count; i++) {

        View child = getChildAt(i);

        if(child.getVisibility() == View.GONE)
            return;

        int currentWidth = child.getMeasuredWidth() + child.getPaddingRight() + child.getPaddingLeft();
        int currentHeight = child.getMeasuredHeight() + child.getPaddingBottom() + child.getPaddingTop();

        if(currentLeft + currentWidth > parentWidth) {
            currentLeft = getPaddingLeft();
            currentTop += currentHeight;
        }

        currentBottom = currentTop + currentHeight;
        currentRight = currentLeft + currentWidth;

        child.layout(currentLeft, currentTop, currentRight, currentBottom);

        currentLeft += currentWidth;
    }
}

      

So you can set android:layout_width="250dp"

and get a result like this:

enter image description here



Install android:layout_width="match_parent"

and get the result like this:

enter image description here

Or use event android:layout_width="wrap_content

and get the result like this:

enter image description here

Hope it helps.

+1


source







All Articles