I have this custom view:
public class TopBarHandler extends FrameLayout {
private Drawable mRightButtonSrc;
private boolean mShowRightButton;
private String mTitle;
private ImageButton mHamburgerButton;
private EllipsizingTextView mTitleTextView;
private ImageButton mRightButton;
private ProgressBar mProgressBar;
public TopBarHandler(final Context context, final AttributeSet attrs) {
super(context, attrs);
initView();
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.topbarMenu);
mShowRightButton = a.getBoolean(R.styleable.topbarMenu_showRightButton, false);
mRightButtonSrc = a.getDrawable(R.styleable.topbarMenu_rightButtonSrc);
mTitle = a.getString(R.styleable.topbarMenu_titleText);
}
public TopBarHandler(final Context context) {
super(context);
initView();
}
private void initView() {
View view = inflate(getContext(), R.layout.top_bar_layout, null);
mHamburgerButton = (ImageButton) findViewById(R.id.top_bar_hamburger_button);
mTitleTextView = (EllipsizingTextView) findViewById(R.id.top_bar_title);
mRightButton = (ImageButton) findViewById(R.id.top_bar_right_button);
mProgressBar = (ProgressBar) findViewById(R.id.top_bar_progress_bar);
addView(view);
}
public interface TopBarListener {
void onHamburgerButtonPressed();
void onRightButtonPressed();
}
}
With this layout definition:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/top_bar_layout"
android:layout_width="match_parent"
android:layout_height="#dimen/height_top_bar"
android:background="#drawable/gradient_top_bar"
android:gravity="center_vertical">
<ImageButton
android:id="#+id/top_bar_hamburger_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="#dimen/margin_objects_top_bar"
android:src="#drawable/selector_menu_topbar"
android:background="#null"/>
<com.myproject.android.utilities.fonts.EllipsizingTextView
android:id="#+id/top_bar_title"
style="#style/bar_title_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="#string/CameraWizard_CameraWizard"
android:textSize="#dimen/menu_top_bar_title_size"
android:layout_marginLeft="#dimen/margin_top_bar_title"
android:layout_toLeftOf="#+id/top_bar_right_button"
android:layout_alignParentLeft="true"/>
<ImageButton
android:id="#+id/top_bar_right_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="#dimen/margin_objects_top_bar"
android:background="#drawable/top_bar_search"
android:visibility="invisible" />
<ProgressBar
android:id="#+id/top_bar_progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="#dimen/margin_objects_top_bar"
android:indeterminate="false"
android:minHeight="50dp"
android:minWidth="50dp"
android:progress="1"
android:visibility="gone" />
</RelativeLayout>
And I want to put it in multiple layouts, so I do it like this:
<com.myproject.android.menu.TopBarHandler
android:layout_height="#dimen/height_top_bar"
android:layout_width="fill_parent"
/>
But the thing is, if I want to put this in multiple layouts (70+) then I have to specify the height and width in every single one of them. Is there a way to enforce the height in the customview itself?
If I dont specify height and width on the target layout, then I get an error. If I put wrap_content the height specified in the topbar_layout is ignored.
You need to implement onMeasure() in your custom view:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// What is the desired size of the view?
int desiredWidth = 250;
int desiredHeight = 250;
// The MeasureSpecs passed in will depend on the LayoutParams
// applied to the view in xml or programmatically
// Analyse the MeasureSpec for width and height separately like this:
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
switch (widthMode) {
case MeasureSpec.UNSPECIFIED:
widthSize = desiredWidth;
break;
case MeasureSpec.AT_MOST:
widthSize = Math.min(widthSize, desiredWidth);
break;
case MeasureSpec.EXACTLY:
break;
default:
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
switch (heightMode) {
case MeasureSpec.UNSPECIFIED:
heightSize = desiredHeight;
break;
case MeasureSpec.AT_MOST:
heightSize = Math.min(heightSize, desiredHeight);
break;
case MeasureSpec.EXACTLY:
break;
default:
}
// You must call setMeasuredDimension()
setMeasuredDimension(widthSize, heightSize);
}
Related
I have a textView which I want to remove every padding and extra space at the top and bottom of the text. This is my XML:
<TextView
android:id="#+id/stid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/quicksand"
android:gravity="center_horizontal"
android:includeFontPadding="false"
android:maxLines="1"
android:text="#string/st"
android:textSize="40sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.538"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
I am wrapping content and removing font padding but there is still a large padding at the top and at the bottom of the text which I want to remove. (on the sides it is fine just top and bottom)
this is a screenshot as requested
Did you try?
android:includeFontPadding="false"
android:lineSpacingExtra="-5dp"
Use this class and compile your project.
public class NoPaddingTextView extends TextView {
private int mAdditionalPadding;
public NoPaddingTextView(Context context) {
super(context);
init();
}
public NoPaddingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setIncludeFontPadding(false);
}
#Override
protected void onDraw(Canvas canvas) {
int yOff = -mAdditionalPadding / 6;
canvas.translate(0, yOff);
super.onDraw(canvas);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
getAdditionalPadding();
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode != MeasureSpec.EXACTLY) {
int measureHeight = measureHeight(getText().toString(), widthMeasureSpec);
int height = measureHeight - mAdditionalPadding;
height += getPaddingTop() + getPaddingBottom();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private int measureHeight(String text, int widthMeasureSpec) {
float textSize = getTextSize();
TextView textView = new TextView(getContext());
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
textView.setText(text);
textView.measure(widthMeasureSpec, 0);
return textView.getMeasuredHeight();
}
private int getAdditionalPadding() {
float textSize = getTextSize();
TextView textView = new TextView(getContext());
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
textView.setLines(1);
textView.measure(0, 0);
int measuredHeight = textView.getMeasuredHeight();
if (measuredHeight - textSize > 0) {
mAdditionalPadding = (int) (measuredHeight - textSize);
Log.v("NoPaddingTextView", "onMeasure: height=" + measuredHeight + " textSize=" + textSize + " mAdditionalPadding=" + mAdditionalPadding);
}
return mAdditionalPadding;
}
}
Replace TextView with NoPaddingTextView Class Name in your XML file
<YourPackage.NoPaddingTextView
android:id="#+id/stid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="#font/quicksand"
android:gravity="center_horizontal"
android:includeFontPadding="false"
android:maxLines="1"
android:text="#string/st"
android:textSize="40sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.538"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Source : Github
StackOverFlow
I'm trying to create custom layout "SquareBox" based on relative layout to hold a ImageView in center.
And this custom layout will always be in square size (depend on ImageView's height and width).
protected void onMeasure(int width, int height) {
super.onMeasure(width, height);
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
int size;
if (measuredWidth > measuredHeight) {
size = measuredWidth;
} else {
size = measuredHeight;
}
setMeasuredDimension(size, size);
}
Here is my layout code:
<com.example.tools.SquareBox
android:id="#+id/square_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:background="#android:color/darker_gray">
<ImageView
android:id="#+id/canvas_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:layout_centerInParent="true"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
tools:src="#drawable/demo_image" />
</com.example.tools.SquareBox>
It is working fine but the image-view is not in center of this custom view.
I've tried following:
->android:layout_centerInParent="true" for ImageView.
->android:gravity="center" for "SquareBox".
->Adding CENTER_IN_PARENT programmatically to ImageView in SquareBox's onMeasure method.
But nothing is working.
What am i missing here?
Change your layout to this (change layout_width to match_parent) :
<com.example.tools.SquareBox
android:id="#+id/square_box"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:background="#android:color/darker_gray">
<ImageView
android:id="#+id/canvas_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:layout_centerInParent="true"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
tools:src="#drawable/demo_image" />
</com.example.tools.SquareBox>
And change your custom layout to this:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int size;
if(widthMode == MeasureSpec.EXACTLY && widthSize > 0){
size = widthSize;
}
else if(heightMode == MeasureSpec.EXACTLY && heightSize > 0){
size = heightSize;
}
else{
size = widthSize < heightSize ? widthSize : heightSize;
}
int finalMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
super.onMeasure(finalMeasureSpec, finalMeasureSpec);
}
Try this:-android:layout_gravity="center_horizontal"
I have emulated the square box behavior in another way. I think it's a workaround and It may be not the best solution but it works. I would like to know better and cleaner solution.
Layout File:
<?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:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/square_box_holder"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="0dp"
android:layout_weight="3">
<View
android:id="#+id/square_box"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_centerInParent="true"
android:background="#android:color/darker_gray" />
<ImageView
android:id="#+id/canvas_image"
style="#style/CanvasImageStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="0dp"
android:adjustViewBounds="true"
tools:src="#drawable/demo_image2" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</LinearLayout>
</LinearLayout>
Code to resize the view:
squareImageView.setImageBitmap(getResultBitmap());
squareBoxHolder.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
squareBoxHolder.getViewTreeObserver().removeOnPreDrawListener(this);
Log.i(TAG, "onPreDraw: squareBoxHolder");
squareBoxHolderSize = new Point(squareBoxHolder.getMeasuredWidth(), squareBoxHolder.getMeasuredHeight());
return true;
}
});
squareImageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
squareImageView.getViewTreeObserver().removeOnPreDrawListener(this);
Log.i(TAG, "onPreDraw: squareImageView");
int imageWidth = squareImageView.getMeasuredWidth();
int imageHeight = squareImageView.getMeasuredHeight();
int size;
ViewGroup.LayoutParams layoutParams = squareImageView.getLayoutParams();
if (imageWidth > imageHeight) {
if (imageWidth > squareBoxHolderSize.y) {
size = squareBoxHolderSize.y;
layoutParams.width = size;
squareImageView.setLayoutParams(layoutParams);
} else {
size = imageWidth;
}
} else {
if (imageHeight > squareBoxHolderSize.x) {
size = squareBoxHolderSize.x;
layoutParams.height = size;
squareImageView.setLayoutParams(layoutParams);
} else {
size = imageHeight;
}
}
layoutParams = squareBox.getLayoutParams();
layoutParams.height = size;
layoutParams.width = size;
squareBox.setLayoutParams(layoutParams);
return true;
}
});
I have following code:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 2"
android:textSize="24sp" />
</LinearLayout>
that results:
How can I have equal size of View's height with its width (that is resulted by layout_weight)?
You have to override the onMeasure method set the same height as the height. Please see this question: Simple way to do dynamic but square layout
This is the complete solution that I come up with:
SquareLinearLayout.java :
public class SquareLinearLayout extends LinearLayout {
public SquareLinearLayout(Context context) {
super(context);
}
public SquareLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthDesc = MeasureSpec.getMode(widthMeasureSpec);
int heightDesc = MeasureSpec.getMode(heightMeasureSpec);
int size = 0;
if (widthDesc == MeasureSpec.UNSPECIFIED
&& heightDesc == MeasureSpec.UNSPECIFIED) {
size = getContext().getResources().getDimensionPixelSize(R.dimen.default_size); // Use your own default size, for example 125dp
} else if ((widthDesc == MeasureSpec.UNSPECIFIED || heightDesc == MeasureSpec.UNSPECIFIED)
&& !(widthDesc == MeasureSpec.UNSPECIFIED && heightDesc == MeasureSpec.UNSPECIFIED)) {
//Only one of the dimensions has been specified so we choose the dimension that has a value (in the case of unspecified, the value assigned is 0)
size = width > height ? width : height;
} else {
//In all other cases both dimensions have been specified so we choose the smaller of the two
size = width > height ? height : width;
}
setMeasuredDimension(size, size);
}
}
activity_my.xml :
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:listitem="#layout/item"
tools:context=".MyActivity">
</ListView>
item.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="match_parent">
<com.appsrise.squarelinearlayout.SquareLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/holo_blue_bright">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</com.appsrise.squarelinearlayout.SquareLinearLayout>
<com.appsrise.squarelinearlayout.SquareLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#android:color/holo_green_light">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 2"
android:textSize="24sp" />
</com.appsrise.squarelinearlayout.SquareLinearLayout>
</LinearLayout>
MyActivity.java :
public class MyActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(new BaseAdapter() {
private LayoutInflater mInflater = LayoutInflater.from(MyActivity.this);
#Override
public int getCount() {
return 100;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = mInflater.inflate(R.layout.item, parent, false);
return convertView;
}
});
}
}
Simpler solution is to pass the width measurement specification into the height specification when calling onMeasure (or the other way around if you want the height to determine the square size).
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class SquareLinearLayout extends LinearLayout{
public SquareLinearLayout(Context context) {
super(context);
}
public SquareLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
// or super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
}
Xamarin version
[Register("SquareFrameLayout")]
public class SquareFrameLayout : LinearLayout
{
public SquareFrameLayout(Context context) : base(context)
{
}
public SquareFrameLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public SquareFrameLayout(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
var width = MeasureSpec.GetSize(widthMeasureSpec);
var height = MeasureSpec.GetSize(heightMeasureSpec);
var widthDesc = MeasureSpec.GetMode(widthMeasureSpec);
var heightDesc = MeasureSpec.GetMode(heightMeasureSpec);
if (widthDesc == MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
//size = Context.Resources.GetDimensionPixelSize(Resource.Dimension.default_size); // Use your own default size, for example 125dp
height = width = 0;
}
else if(heightDesc != MeasureSpecMode.Unspecified && widthDesc == MeasureSpecMode.Unspecified)
{
width = height;
}
else if (widthDesc != MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
height = width;
}
else
{
//In all other cases both dimensions have been specified so we choose the smaller of the two
var size = width > height ? height : width;
width = height = size;
}
SetMeasuredDimension(width, height);
}
and
[Register("SquareLinearLayout")]
public class SquareLinearLayout : LinearLayout
{
public SquareLinearLayout(Context context) : base(context)
{
}
public SquareLinearLayout(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
public SquareLinearLayout(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
}
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
var width = MeasureSpec.GetSize(widthMeasureSpec);
var height = MeasureSpec.GetSize(heightMeasureSpec);
var widthDesc = MeasureSpec.GetMode(widthMeasureSpec);
var heightDesc = MeasureSpec.GetMode(heightMeasureSpec);
if (widthDesc == MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
//size = Context.Resources.GetDimensionPixelSize(Resource.Dimension.default_size); // Use your own default size, for example 125dp
height = width = 0;
}
else if(heightDesc != MeasureSpecMode.Unspecified && widthDesc == MeasureSpecMode.Unspecified)
{
width = height;
}
else if (widthDesc != MeasureSpecMode.Unspecified && heightDesc == MeasureSpecMode.Unspecified)
{
height = width;
}
else
{
//In all other cases both dimensions have been specified so we choose the smaller of the two
var size = width > height ? height : width;
width = height = size;
}
SetMeasuredDimension(width, height);
}
}
You can do this also, If you need only one TextView needs to be square :
ViewTreeObserver vto = textview.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
textview.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = textview.getMeasuredWidth();
int height = textview.getMeasuredHeight();
ViewGroup.LayoutParams params = textview.getLayoutParams();
if (width > height)
params.height = width;
else
params.width = height;
textview.setLayoutParams(params);
}
});
You can divide a LinearLayout into two equal LinearLayout like this..
<LinearLayout android:orientation="horizontal" android:layout_height="fill_parent" android:layout_width="fill_parent">
<LinearLayout android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</LinearLayout>
<LinearLayout android:layout_weight="1" android:layout_height="fill_parent" android:layout_width="fill_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Test 1"
android:textSize="24sp" />
</LinearLayout>
</LinearLayout>
This will divide the LinearLayout into two equal LinearLayouts and each of these LinearLayouts will contain the TextViews
I'm newbie to android and have a problem with it.
I'm trying to make a customized view in android for menu.
I want it to have 4 elements which have images.
But these are invisible though I coded like below :( What can I do for it ?
I've googled it for a while and there's no reasonable solution in my case ...
Menuindicator.java
public class MenuIndicator extends FrameLayout {
private static final int MIN_WIDTH = 50;
private static final int MIN_HEIGHT = 50;
private final float ACTUAL_PIXELS = getContext().getResources().getDisplayMetrics().density;
private ImageView menuComponentAbove = null;
private ImageView menuComponentBelow = null;
private ImageView menuComponentFront = null;
private ImageView menuComponentBack = null;
public MenuIndicator(Context context) {
super(context);
init(null, 0);
}
public MenuIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0);
}
public MenuIndicator(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle) {
final TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MenuIndicator, defStyle, 0);
LayoutInflater.from(getContext()).inflate(R.layout.layout_menu_indicator, this);
menuComponentAbove = (ImageView) findViewById(R.id.menu_component_above);
menuComponentBelow = (ImageView) findViewById(R.id.menu_component_below);
menuComponentFront = (ImageView) findViewById(R.id.menu_component_front);
menuComponentBack = (ImageView) findViewById(R.id.menu_component_back);
typedArray.recycle();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = 0;
switch(widthMode) {
case MeasureSpec.UNSPECIFIED: widthSize = widthMeasureSpec; break;
case MeasureSpec.AT_MOST: widthSize = (int)(MIN_WIDTH * ACTUAL_PIXELS); break;
case MeasureSpec.EXACTLY: widthSize = MeasureSpec.getSize(widthMeasureSpec); break;
}
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = 0;
switch(heightMode) {
case MeasureSpec.UNSPECIFIED: heightSize = heightMeasureSpec; break;
case MeasureSpec.AT_MOST: heightSize = (int)(MIN_HEIGHT * ACTUAL_PIXELS); break;
case MeasureSpec.EXACTLY: heightSize = MeasureSpec.getSize(heightMeasureSpec); break;
}
setMeasuredDimension(widthSize, heightSize);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
menuComponentAbove.draw(canvas);
menuComponentBelow.draw(canvas);
menuComponentFront.draw(canvas);
menuComponentBack.draw(canvas);
invalidate();
}
}
layout_menu_indicator.xml
<!-- layout_menu_indicator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
android:background="#171717">
<RelativeLayout
android:id="#+id/menu_indicator"
android:layout_width="#dimen/dimen_menu_indicator"
android:layout_height="#dimen/dimen_menu_indicator"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true">
<ImageView
android:id="#+id/menu_component_front"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_centerVertical="true"
android:antialias="true"
android:src="#drawable/menu_component" />
<ImageView
android:id="#+id/menu_component_back"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_centerVertical="true"
android:antialias="true"
android:src="#drawable/menu_component" />
<ImageView
android:id="#+id/menu_component_above"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_above="#id/menu_component_front"
android:layout_centerVertical="true"
android:antialias="true"
android:src="#drawable/menu_component" />
<ImageView
android:id="#+id/menu_component_below"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_below="#id/menu_component_front"
android:layout_centerVertical="true"
android:antialias="true"
android:src="#drawable/menu_component" />
</RelativeLayout>
<LinearLayout
android:id="#+id/command_container_horizontal"
android:layout_width="wrap_content"
android:layout_height="#dimen/dimen_menu_indicator"
android:layout_alignParentTop="true"
android:layout_toLeftOf="#+id/menu_indicator"
android:orientation="horizontal">
</LinearLayout>
<LinearLayout
android:id="#+id/command_container_vertical"
android:layout_width="#dimen/dimen_menu_indicator"
android:layout_height="wrap_content"
android:layout_below="#+id/menu_indicator"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:orientation="vertical">
</LinearLayout>
</RelativeLayout>
Add this:
public MenuIndicator(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(getContext()).inflate(R.layout.layout_menu_indicator, this);
menuComponentAbove = (ImageView) findViewById(R.id.menu_component_above);
menuComponentBelow = (ImageView) findViewById(R.id.menu_component_below);
menuComponentFront = (ImageView) findViewById(R.id.menu_component_front);
menuComponentBack = (ImageView) findViewById(R.id.menu_component_back);
}
And remove that code from your init() method
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