Well
I m just learning custom viewgroups in android so i have made a simple viewgroup. i named it LearnLayout
i have custom layoutparam in it, called as LearnLayoutParams
Problem
when i use custom attributes on children. those attributes are not being parsed in LearnLayoutParams adn i get default values
Code
here are my code files
LearnLayout.java
this is the cusom viewgroup class file
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.RequiresApi;
import com.test.learnviewpagertransformations.MainActivity;
import com.test.learnviewpagertransformations.R;
public class LearnLayout extends ViewGroup {
private static final String TAG = MainActivity.TAG;
public LearnLayout(Context context) {
super(context);
}
public LearnLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LearnLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int maxWidth = 0;
int maxHeight = 0;
for(int i=0; i<getChildCount(); i++){
View childView = getChildAt(i);
if(childView.getVisibility() != GONE){
measureChildWithMargins(childView, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LearnLayoutParams lp = (LearnLayoutParams) childView.getLayoutParams();
int childWidth = lp.leftMargin + childView.getMeasuredWidth() + lp.rightMargin;
int childHeight = lp.topMargin + childView.getMeasuredHeight() + lp.bottomMargin;
maxWidth = Math.max(childWidth, maxWidth);
maxHeight = maxHeight + childHeight;
}
}
setMeasuredDimension(maxWidth, maxHeight);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int fromLeft = getPaddingLeft();
int fromTop = getPaddingTop();
int fromRight = right - left - getPaddingRight();
int fromBottom = bottom - top - getPaddingBottom();
for(int i=0; i<getChildCount(); i++){
View childView = getChildAt(i);
if (childView.getVisibility() != GONE) {
final LearnLayoutParams lp = (LearnLayoutParams) childView.getLayoutParams();
childView.layout(
fromLeft + lp.leftMargin,
fromTop + lp.topMargin,
fromRight - lp.rightMargin,
fromTop + lp.topMargin + lp.height
);
childView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
Log.i(TAG, "onLayout: " + lp.backgroundColor);
}
});
childView.setBackgroundColor(getColor(lp.backgroundColor));
fromTop = fromTop + childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
}
}
}
#Override
public LearnLayoutParams generateLayoutParams(AttributeSet attrs) {
return new LearnLayoutParams(getContext(), attrs);
}
#Override
protected LearnLayoutParams generateDefaultLayoutParams() {
return new LearnLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
#Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new LearnLayoutParams(p);
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LearnLayoutParams;
}
public static class LearnLayoutParams extends MarginLayoutParams {
public int backgroundColor = 0;
public LearnLayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(R.styleable.LearnLayoutLP);
backgroundColor = a.getInt(R.styleable.LearnLayoutLP_layout_background_color, backgroundColor);
Log.i(TAG, "LayoutParams: " + backgroundColor);
a.recycle();
}
public LearnLayoutParams(int width, int height) {
super(width, height);
}
public LearnLayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
private int getColor(int backgroundColor){
switch (backgroundColor){
case 0:
return Color.RED;
case 1:
return Color.BLUE;
case 2:
return Color.GREEN;
default:
return Color.BLACK;
}
}
}
activity_learn_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.test.learnviewpagertransformations.views.LearnLayout
android:background="#99ff99"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<View
app:layout_background_color="red"
android:layout_margin="50dp"
android:layout_width="100dp"
android:layout_height="100dp" />
<LinearLayout
android:id="#+id/testView"
android:gravity="center"
app:layout_background_color="blue"
android:layout_margin="50dp"
android:layout_width="100dp"
android:layout_height="100dp">
<TextView
android:textStyle="bold"
android:textColor="#fff"
android:text="Test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<View
app:layout_background_color="green"
android:layout_margin="50dp"
android:layout_width="100dp"
android:layout_height="100dp" />
</com.test.learnviewpagertransformations.views.LearnLayout>
<!--<com.test.learnviewpagertransformations.views.CustomLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!– put first view to left. –>
<TextView
android:background="#drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="left"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="l1"/>
<!– stack second view to left. –>
<TextView
android:background="#drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="left"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="l2"/>
<!– also put a view on the right. –>
<TextView
android:background="#drawable/filled_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_position="right"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="r1"/>
<!– by default views go in the middle; use fill vertical gravity –>
<TextView
android:background="#drawable/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="fill_vertical|center_horizontal"
android:text="fill-vert"/>
<!– by default views go in the middle; use fill horizontal gravity –>
<TextView
android:background="#drawable/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|fill_horizontal"
android:text="fill-horiz"/>
<!– by default views go in the middle; use top-left gravity –>
<TextView
android:background="#drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|left"
android:text="top-left"/>
<!– by default views go in the middle; use center gravity –>
<TextView
android:background="#drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="center"/>
<!– by default views go in the middle; use bottom-right –>
<TextView
android:background="#drawable/blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:text="bottom-right"/>
</com.test.learnviewpagertransformations.views.CustomLayout>-->
</LinearLayout>
attrs.xml
custom attributes that i have made for my layoutparams
<declare-styleable name="LearnLayoutLP">
<attr name="layout_background_color">
<enum name="red" value="0" />
<enum name="blue" value="1" />
<enum name="green" value="2" />
</attr>
</declare-styleable>
What i think
there is something i m missing in onLayout and onMeasure and because of that i m not getting the correct values of custom attributes. If you check my custom layoutparams class even at that place i dont get the values i have used all i get in logs is default value that it 0
Request
Please share whatever you have in your mind
I ll gladly welcome any amendments and improvements
_/\_
Related
I have the following XML file that is built out of a custom layout that is created via Java code and 3 buttons inside a linear layout below it -
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="#color/grey_200"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="#layout/toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/grey_200"
android:gravity="center_vertical"
android:orientation="vertical"
tools:layout_height="500dp">
<com.etiennelawlor.tinderstack.ui.TinderStackLayout
android:id="#+id/activity_main_tinder_stack_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="5dp">
<Button
android:layout_width="wrap_content"
android:id="#+id/activity_main_delete_button"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#ff0000"
android:tag="1"
android:text="#string/activity_main_delete" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/activity_main_pass_button"
android:layout_margin="10dp"
android:background="#A9A9A9"
android:tag="2"
android:text="#string/activity_main_pass" />
<Button
android:layout_width="wrap_content"
android:id="#+id/activity_main_approve_button"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#98FB98"
android:tag="3"
android:text="#string/activity_main_approve" />
</LinearLayout>
</LinearLayout>
The issue I am facing looks like this -
I want the image to be able to move on top of the buttons when touching it, but also of course for the buttons to be touchable and functional.
Here is my TinderStackLayout class -
public class TinderStackLayout extends FrameLayout {
// Constants
private static final int DURATION = 300;
// Variable members
private OnCardSwipedListener onCardSwipedListener;
private int screenWidth;
private int yMultiplier;
//Top card
private TinderCardView topCardOnStack;
private Button mDeleteButton, mPassButton, mApproveButton;
//Constructors
public TinderStackLayout(Context context) {
super(context);
init();
}
public TinderStackLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TinderStackLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
super.addView(child, index, params);
if (onCardSwipedListener != null)
onCardSwipedListener.onNext(getChildCount());
}
#Override
public void removeView(View view) {
super.removeView(view);
if (onCardSwipedListener != null)
onCardSwipedListener.onNext(getChildCount());
}
#Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
// Helper Methods
private void init() {
setClipChildren(false);
screenWidth = DisplayUtility.getScreenWidth(getContext());
yMultiplier = DisplayUtility.dp2px(getContext(), 8);
mDeleteButton = new Button(getContext());
mPassButton = new Button(getContext());
mApproveButton = new Button(getContext());
}
public void addCard(TinderCardView tinderCardView) {
View firstCard = getChildAt(0);
if (firstCard != null && firstCard.equals(tinderCardView)) {
return;
}
if (onCardSwipedListener == null)
onCardSwipedListener = tinderCardView.getOnCardSwipedListener();
topCardOnStack = tinderCardView;
ViewGroup.LayoutParams layoutParams;
layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int childCount = getChildCount();
addView(tinderCardView, 0, layoutParams);
float scaleValue = 1 - (childCount / 50.0f);
tinderCardView.animate()
.x(0)
.y(childCount * yMultiplier)
.scaleX(scaleValue)
.setInterpolator(new AnticipateOvershootInterpolator())
.setDuration(DURATION);
}
public TinderCardView getTopCardOnStack() {
return topCardOnStack;
}
}
Do I need to add them dynamically? or is there a simpler way?
And if I do need to add them dynamically - I would be happy to get a code example of how to do so.
I need implement group chat view like what's app.
Please find below example code which I am tried
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingBottom="#dimen/size_05"
android:paddingTop="#dimen/size_05"
android:layout_marginRight="#dimen/size_80"
android:background="#drawable/chat_white_border"
android:orientation="vertical">
<TextView
android:id="#+id/txt_friends_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="false"
android:paddingLeft="#dimen/size_10"
android:paddingRight="#dimen/size_10"
android:text="name"
android:textColor="#color/chat_friends_name"
android:textSize="#dimen/size_12" />
<view
android:id="#+id/ll_c_friends_chat_message"
class="com.view.ChatBubbleLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:orientation="vertical">
<TextView
android:id="#+id/txt_friends_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingLeft="#dimen/size_10"
android:paddingRight="#dimen/size_10"
android:text="name1" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center|right"
android:orientation="horizontal">
<TextView
android:id="#+id/txt_rec_chat_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="#dimen/size_10"
android:text="11.00 PM"
android:textColor="#color/grey"
android:textSize="#dimen/size_10" />
</LinearLayout>
</view>
</LinearLayout>
</LinearLayout>
Custom view file:
package com.view;
import android.annotation.TargetApi;
import android.content.Context;
import android.text.Layout;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
/**
* Created by Priyan
*/
public class ChatBubbleLayout extends FrameLayout {
public ChatBubbleLayout(Context context) {
super(context);
}
public ChatBubbleLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChatBubbleLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(21)
public ChatBubbleLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
TextView message_textView = (TextView) getChildAt(0);
View localView = getChildAt(1);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int view_size = View.MeasureSpec.getSize(widthMeasureSpec);
Layout messageTextviewLayout = message_textView.getLayout();
int linestart = messageTextviewLayout.getLineStart(messageTextviewLayout.getLineCount() - 1);
int lineend = messageTextviewLayout.getLineEnd(messageTextviewLayout.getLineCount() - 1);
int desiredWidth = (int) Layout.getDesiredWidth(message_textView.getText()
.subSequence(linestart, lineend), message_textView.getPaint());
int measuredWidth = message_textView.getMeasuredWidth();
int requiredWidth = Math.min(measuredWidth,
(int) Math.ceil(Layout.getDesiredWidth(message_textView.getText(),
message_textView.getPaint())) + message_textView.getPaddingRight() +
message_textView.getPaddingLeft());
if (view_size - getPaddingLeft() - getPaddingRight()
>= requiredWidth + localView.getMeasuredWidth()) {
setMeasuredDimension(requiredWidth + localView.getMeasuredWidth() +
getPaddingLeft() + getPaddingRight(), getMeasuredHeight());
} else if ((requiredWidth - message_textView.getPaddingLeft() - message_textView.getPaddingRight()
< desiredWidth + localView.getMeasuredWidth())) {
setMeasuredDimension(getMeasuredWidth(),
getMeasuredHeight() + localView.getMeasuredHeight());
}
}
}
I got result like:
You can see that time is not aligned properly to exteme right of the view on received message...
I need group chat view similar like what's app...
I have created custom view which extends FrameLayout. After adding it into RelativeLayout it's being displayed as two nested views:
Is it normal? It sometimes messes up with wrap_content flags but I couldn't figure out why. When I use View as a base class everything looks normal.
Here is my code:
MainActivity.java
package com.example.app;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.SeekBar;
import com.codersmill.tset.R;
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.app.RateBar
android:layout_width="match_parent"
android:layout_height="48dp"
android:id="#+id/rateBar"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
</RelativeLayout>
RateBar.java
package com.example.app;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.codersmill.tset.R;
public class RateBar extends FrameLayout {
TextView star1, star2, star3, star4, star5;
View dot;
private boolean isAnimating = false;
int radius = 36;
private int step = 100;
private int leftMargin = 50;
private int topMargin = 0;
private int currentPosition = 0;
public RateBar(Context context) {
this(context, null);
}
public RateBar(Context context, AttributeSet attrs) {
super(context, attrs);
View.inflate(context, R.layout.view_rate_bar, this);
star1 = (TextView) this.findViewById(R.id.star1);
star2 = (TextView) this.findViewById(R.id.star2);
star3 = (TextView) this.findViewById(R.id.star3);
star4 = (TextView) this.findViewById(R.id.star4);
star5 = (TextView) this.findViewById(R.id.star5);
star1.setOnClickListener(onClickListener);
star2.setOnClickListener(onClickListener);
star3.setOnClickListener(onClickListener);
star4.setOnClickListener(onClickListener);
star5.setOnClickListener(onClickListener);
dot = this.findViewById(R.id.selector);
}
public RateBar(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs);
}
private OnClickListener onClickListener = new OnClickListener() {
#Override public void onClick(View v) {
if(isAnimating) return;
switch (v.getId()) {
case R.id.star1:
animateToPosition(0);
break;
case R.id.star2:
animateToPosition(1);
break;
case R.id.star3:
animateToPosition(2);
break;
case R.id.star4:
animateToPosition(3);
break;
case R.id.star5:
animateToPosition(4);
break;
}
}
};
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
step = (int) (getMeasuredWidth() / 5.0f);
leftMargin = (int) (step / 2.0f - radius);
topMargin = (int) (getMeasuredHeight() / 2.0f - radius);
if(!isAnimating) {
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.leftMargin = leftMargin + currentPosition * step;
params.topMargin = topMargin;
dot.setLayoutParams(params);
}
}
private void animateToPosition(final int position) {
final int from = currentPosition*step + leftMargin;
final int to = position*step + leftMargin;
ValueAnimator animation = ValueAnimator.ofInt(from, to);
animation.setDuration(250);
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override public void onAnimationUpdate(ValueAnimator animation) {
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.leftMargin = (int) animation.getAnimatedValue();
params.topMargin = topMargin;
dot.setLayoutParams(params);
}
});
animation.addListener(new AnimatorListenerAdapter() {
#Override public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
isAnimating = true;
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.topMargin = topMargin;
params.leftMargin = currentPosition * step + leftMargin;
dot.setLayoutParams(params);
}
#Override public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isAnimating = false;
currentPosition = position;
FrameLayout.LayoutParams params = (LayoutParams) dot.getLayoutParams();
params.leftMargin = currentPosition * step + leftMargin;
params.topMargin = topMargin;
dot.setLayoutParams(params);
}
});
animation.start();
}
private void updateDotPosition() {
}
}
view_rate_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content">
<View
android:id="#+id/selector"
android:layout_width="24dp"
android:layout_height="24dp"
android:background="#drawable/oval"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="1"
android:id="#+id/star1"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#2200ff00"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="2"
android:id="#+id/star2"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#22ff0000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="3"
android:id="#+id/star3"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#2200ff00"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="4"
android:id="#+id/star4"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#22ff0000"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="5"
android:id="#+id/star5"
android:layout_weight="1"
android:gravity="center"
android:padding="12dp"
android:background="#2200ff00" />
</LinearLayout>
</merge>
even i was facing the same issue, it appears that new version of android studio comes up with two files content_main.xml and activity_mail.xml , when we select Activity_main.xml>Design view everything appears 'Custom View' , instead when we highlight content_main.xml>Design, everything is bac to normal. I dont know why it happens but that;s how i fix mine ( android nooob here )
More can be found here : https://teamtreehouse.com/community/i-cant-drag-widgets-onto-the-phone-mockup-component-tree-shows-customview-instead-of-the-relative-view-help
historically i have stupid problems with layouts, and now i try to fill this blank in my brain. Watch this example, i try to do some axis for graphic, and i want to pack them like this:
Vertical line by left side
Horizontal line by bottom
Spacer between Vert & Horiz in left bottom corner
Other place fill by graph
(Sorry not allowed to post images.I hope you see graphics axis before, no need to explain.)
And this what i got at now:
<?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="vertical" >
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.example.graphicformatting.Axis
android:id="#+id/yaxis"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="40dp"
android:background="#color/backpanel_dark"
custom:ishorizontal="false" />
<EditText
android:id="#+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textMultiLine"
android:text="This text field mean my own graphic widget, which not have any OnMeasured function or any specific and it's painting with size, which assigment by parent/ Just use getHeight and getWeight" >
<requestFocus />
</EditText>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<View
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#color/backpanel_dark" />
<com.example.graphicformatting.Axis
android:id="#+id/yaxis"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="40dp"
android:background="#color/backpanel_dark"
custom:ishorizontal="true" />
</LinearLayout>
And this is code of my axises:
package com.example.graphicformatting;
public class Axis extends View {
public static final int HORIZONTAL = 0;
public static final int VERTICAL = 1;
public static final int DP_SIZE = 40;
int type = -1;
int size;
Paint painter;
public Axis(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.com_example_graphicformatting_Axis);
final int N = a.getIndexCount();
for (int i = 0; i < N; ++i) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.com_example_graphicformatting_Axis_ishorizontal:
if (a.getBoolean(attr, true)) {
type = HORIZONTAL;
} else
type = VERTICAL;
break;
default:
Log.d("axis", "Found unexpected state");
}
}
a.recycle();
final float scale = getContext().getResources().getDisplayMetrics().density;
size = (int) (DP_SIZE * scale + 0.5f);
painter = new Paint();
painter.setColor(Color.RED);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
if (type == HORIZONTAL) {
this.setMeasuredDimension(parentWidth, size);
this.setLayoutParams(new LinearLayout.LayoutParams(parentWidth, size));
// Log.d("axis", "Size of horizontal calc: " + (parentWidth) + " "
// + size);
} else if (type == VERTICAL) {
this.setMeasuredDimension(size, parentHeight);
this.setLayoutParams(new LinearLayout.LayoutParams(size, parentHeight));
// Log.d("axis", "Size of vertical calc: " + size + " "
// + (parentHeight));
} else { // Для нормального отображения в конструкторе
this.setMeasuredDimension(size, size);
this.setLayoutParams(new LinearLayout.LayoutParams(size, size));
}
}
#Override
protected void onDraw(Canvas canvas) {
if (type == VERTICAL) {
canvas.drawRect(0, 0, size, getHeight(), painter);
} else {
canvas.drawRect(0, getHeight() - size, getWidth(), getHeight(), painter);
}
super.onDraw(canvas);
}
}
How to custom my Layouts to get right pic?
Ou, this using for set horizontal/vertical attribute (value/attrs.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="com.example.graphicformatting.Axis">
<attr name="ishorizontal" format="boolean"></attr>
</declare-styleable>
</resources>
I am uncertain of what type of layout to use for this certain scenario.
I basically want to have a horizontal linear layout that i can add views to. in this case buttons (displaying tags in an application) But each view will have a different width bases on the name of the tag it is displaying, so i want to add say 10 tags, I need a layout that will fit as many as it can on the 1st line, and then if it doesn't fit, automatically overflow to the next line.
Basically how a text view works with text, if the text is longer than the width it goes to the next line, except I want to do this with non-clickable buttons.
I thought of a grid layout, but then it would have the same no of "tags" on each line when you could have 2 tags with a long name on the first line and 7 with a short name on the second line.
Something that looks a bit like this:
I basically want the look of how stack overflow does it below here.
Answer: Your own custom Layout :)
I know this is a late answer to this question. But it might help the OP or someone for sure.
You can extend ViewGroup to create a custom layout like this one below. The advantage of this is you get the keep the view hierarchy flat.
MyFlowLayout
public class MyFlowLayout extends ViewGroup {
public MyFlowLayout(Context context) {
super(context);
}
public MyFlowLayout(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public MyFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int realWidth = MeasureSpec.getSize(widthMeasureSpec);
int currentHeight = 0;
int currentWidth = 0;
int currentChildHookPointx = 0;
int currentChildHookPointy = 0;
int childCount = this.getChildCount();
for(int i = 0; i < childCount; i++) {
View child = getChildAt(i);
this.measureChild(child, widthMeasureSpec, heightMeasureSpec);
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
//check if child can be placed in the current row, else go to next line
if(currentChildHookPointx + childWidth > realWidth) {
//new line
currentWidth = Math.max(currentWidth, currentChildHookPointx);
//reset for new line
currentChildHookPointx = 0;
currentChildHookPointy += childHeight;
}
int nextChildHookPointx;
int nextChildHookPointy;
nextChildHookPointx = currentChildHookPointx + childWidth;
nextChildHookPointy = currentChildHookPointy;
currentHeight = Math.max(currentHeight, currentChildHookPointy + childHeight);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.x = currentChildHookPointx;
lp.y = currentChildHookPointy;
currentChildHookPointx = nextChildHookPointx;
currentChildHookPointy = nextChildHookPointy;
}
currentWidth = Math.max(currentChildHookPointx, currentWidth);
setMeasuredDimension(resolveSize(currentWidth, widthMeasureSpec),
resolveSize(currentHeight, heightMeasureSpec));
}
#Override
protected void onLayout(boolean b, int left, int top, int right, int bottom) {
//call layout on children
int childCount = this.getChildCount();
for(int i = 0; i < childCount; i++) {
View child = this.getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y + child.getMeasuredHeight());
}
}
#Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyFlowLayout.LayoutParams(getContext(), attrs);
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new MyFlowLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
#Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return new MyFlowLayout.LayoutParams(p);
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof MyFlowLayout.LayoutParams;
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
int spacing = -1;
int x = 0;
int y = 0;
LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray t = c.obtainStyledAttributes(attrs, R.styleable.FlowLayout_Layout);
spacing = t.getDimensionPixelSize(R.styleable.FlowLayout_Layout_layout_space, 0);
t.recycle();
}
LayoutParams(int width, int height) {
super(width, height);
spacing = 0;
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
}
Usage in a layout.xml file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:cl="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.merryapps.customlayout.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
<com.merryapps.customlayout.MyFlowLayout
android:id="#+id/flw1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FF0000">
<Button
android:id="#+id/b1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello"
cl:layout_space="20dp"/>
<Button
android:id="#+id/b2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
<Button
android:id="#+id/b4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
<Button
android:id="#+id/b5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
<Button
android:id="#+id/b6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"/>
</com.merryapps.customlayout.MyFlowLayout>
<Button
android:id="#+id/b3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/world"
android:textAllCaps="false"/>
</LinearLayout>
For show type of view one must go for flow layout :-
There are many libraries available on Git
Following is the example of,
blazsolar/FlowLayout
Add this line in app.gradle
compile "com.wefika:flowlayout:<version>"
Usage:-
<com.wefika.flowlayout.FlowLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|top">
<View
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Lorem ipsum" />
</com.wefika.flowlayout.FlowLayout>
For detail implementation follow below link-
https://github.com/blazsolar/FlowLayout
You can try this links too:-
https://github.com/xiaofeng-han/AndroidLibs/tree/master/flowlayoutmanager (try this)
https://github.com/ApmeM/android-flowlayout
https://gist.github.com/hzqtc/7940858