Due to margin and padding problems with the regular Button class I've been working on a custom Layout that extends FrameLayout.
I ran into a problem, that the contents of the layout do not get displayed.
As soon as I'm changing this to another e.g. RelativeLayout the contents get displayed.
This is my relevant code:
private static final int COLOR = R.styleable.MyButton_color;
private static final int TEXT = R.styleable.MyButton_text;
public MyButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutParams backgroundParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
backgroundParams.gravity = Gravity.FILL;
background = new ImageView(context, null, defStyle);
addView(background, backgroundParams);
text = new TextView(context, null, defStyle);
text.setTypeface(null, Typeface.BOLD);
LayoutParams textParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
textParams.gravity = Gravity.CENTER;
addView(text, textParams);
TypedArray a = context.obtainStyledAttributes(R.styleable.MyButton);
try {
setColor(Color.values()[a.getInteger(COLOR, 0)]);
setText(a.getString(TEXT));
} finally {
a.recycle();
}
}
public void setText(CharSequence text) {
this.text.setText(text);
}
private void setColor(Color color) {
switch (color) {
case ORANGE:
text.setTextColor(getContext().getResources().getColor(R.color.text_blue));
background.setImageResource(R.drawable.button_orange);
break;
case BLUE:
//TODO set colors
break;
}
}
What am I missing on this one?
Extending FrameLayout, this is a special layout which has set a flag on most API versions to not call draw functions.
To overcome this, set the flag to false when constructing:
public MyButton(
...
setWillNotDraw(false);
...
The answer is quite obvious.
LayoutParams backgroundParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
has to be replaced by
LayoutParams backgroundParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
The same applies for textParams, of course.
Related
Is it possible to create a custom view such that it could be refer to by
<components.layouts.CustomView
android:text="#string/sign_in_options" />
without explicitly stating the layout_width and layout_height in xml since this is already defined in the CustomView class as such
public class CustomView extends TextView {
public CustomView(Context context) {
super(context);
setup();
}
public CustomView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
setup();
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup();
}
public CustomView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setup();
}
private void setup() {
setLayoutParams(new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.MATCH_PARENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT));
setBackgroundColor(getResources().getColor(R.color.background));
setTextAlignment(TextView.TEXT_ALIGNMENT_CENTER);
setAllCaps(true);
int i = (int)getResources().getDimension(R.dimen.inner_space);
setPadding(i, i, i, i);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
ViewGroup.MarginLayoutParams margins = ViewGroup.MarginLayoutParams.class.cast(getLayoutParams());
int h = (int)getResources().getDimension(R.dimen.horizontal_space);
margins.setMargins(0, h, 0, h);
setLayoutParams(margins);
}
}
does anyone know if there's a way to do it?
try this:
#Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChild(yourChild,parentWidthMeasureSpec,parentHeightMeasureSpec);
}
you can try something like below. I use this method to insert a view programmatically. U can do based upon your requirement.
View insertPhoto(String path) {
Bitmap bm = ImageUtils.decodeSampledBitmapFromUri(path, 100, 100);
// Main Layout(Linear)
layout = new LinearLayout(getApplicationContext());
layout.setLayoutParams(new ViewGroup.LayoutParams(170, ViewGroup.LayoutParams.MATCH_PARENT));
layout.setGravity(Gravity.CENTER);
layout.setOrientation(LinearLayout.VERTICAL);
// Sub Layout(Relative)
relativeLayout = new RelativeLayout(getApplicationContext());
relativeLayout.setLayoutParams(new ViewGroup.LayoutParams(150, 150));
// Photo (ImageView)
RelativeLayout.LayoutParams imgParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
imgParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
imgParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
imgParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
imgParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
imageView = new ImageView(getApplicationContext());
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
imageView.setBackground(getResources().getDrawable(R.drawable.img_bg));
imageView.setImageBitmap(bm);
imageView.setBackgroundColor(ImageUtils.getDominantColor(bm));
//Log.e("", imageView.getId() + " +++++");
// Sync Icon (ImageView)
RelativeLayout.LayoutParams iconParams = new RelativeLayout.LayoutParams(50, 50);
iconParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
iconParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
syncIcon = new ImageView(getApplicationContext());
//syncIcon.setTag(imageName);
//Log.e("tag", syncIcon.getTag() + " +++++");
//syncIcon.setImageResource(R.drawable.ic_cloud_done);
//syncIcon.setImageResource(R.drawable.ic_cloud_off);
syncIcon.setImageResource(R.drawable.ic_cloud_queue);
//syncIcon.setImageResource(R.drawable.ic_cloud_upload);
//syncIcon.setImageResource(R.drawable.ic_sync);
//syncIcon.setImageResource(R.drawable.ic_alert);
//syncIcon.setImageResource(R.drawable.ic_action_tick);
//imageView.setLayoutParams(imgParams);
//syncIcon.setLayoutParams(iconParams);
// Placing both ImageViews inside Relative Layout
relativeLayout.addView(imageView, imgParams);
relativeLayout.addView(syncIcon, iconParams);
// Placing Relative Layout finally inside Linear Layout
layout.addView(relativeLayout);
return layout;
}
Okay, here is what I am doing... I have two custom layouts:
HandedLayout extends ViewGroup
and
HandedPathLayout extends HandedLayout
So in the root layout, I have a custom LayoutParameters to accept a layout property, called handed. Here is the Layout Parameters class in
public static class LayoutParams extends ViewGroup.LayoutParams {
public int handed;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a =
c.obtainStyledAttributes(attrs, R.styleable.HandedLayout_LayoutParams);
handed = a.getInt(R.styleable.HandedLayout_LayoutParams_layout_handed,
HandedLayout.RIGHT_HANDED);
a.recycle();
}
public LayoutParams(int width, int height) {
super(width, height);
}
}
And as I understand it (shakily, it seems), I also need to override 3 classes in HandedLayout. Here they are:
#Override
public HandedLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new HandedLayout.LayoutParams(getContext(), attrs);
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected HandedLayout.LayoutParams generateDefaultLayoutParams() {
return new HandedLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
So in the inner custom layout (HandedPathLayout), I need lots of layout parameters, so I do the exact same thing! Here is the LayoutParameters class inside of HandedPathLayout:
public static class LayoutParams extends HandedLayout.LayoutParams {
public int[] endSides;
public int[] endPositions;
public int[] anchorSides;
public int[] anchorPositions;
public int controlPointDistance;
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
endSides = new int[2];
endPositions = new int[2];
anchorSides = new int[2];
anchorPositions = new int[2];
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HandedPathLayout_LayoutParams);;
try {
//this is the second place that the default handedness is set, watch out!
//handed = a.getInteger(R.styleable.HandedLayout_LayoutParams_layout_handed, HandedLayout.RIGHT_HANDED);
endSides[0] = a.getInteger(R.styleable.HandedPathLayout_LayoutParams_layout_from_side, HandedLayout.BOTTOM_SIDE);
endSides[1] = a.getInteger(R.styleable.HandedPathLayout_LayoutParams_layout_to_side, HandedLayout.PINKIE_SIDE);
endPositions[0] = a.getDimensionPixelSize(R.styleable.HandedPathLayout_LayoutParams_layout_from_position, 0);
endPositions[1] = a.getDimensionPixelSize(R.styleable.HandedPathLayout_LayoutParams_layout_to_position, 0);
anchorSides[0] = a.getInteger(R.styleable.HandedPathLayout_LayoutParams_layout_anchor_up_side, HandedLayout.TOP_SIDE);
anchorSides[1] = a.getInteger(R.styleable.HandedPathLayout_LayoutParams_layout_anchor_down_side, HandedLayout.BOTTOM_SIDE);
//todo: should these positions be set relative to handedness? which direction is zero?
anchorPositions[0] = a.getDimensionPixelSize(R.styleable.HandedPathLayout_LayoutParams_layout_anchor_up_position, 0);
anchorPositions[1] = a.getDimensionPixelSize(R.styleable.HandedPathLayout_LayoutParams_layout_anchor_down_position, 0);
controlPointDistance = a.getInteger(R.styleable.HandedPathLayout_LayoutParams_layout_control_point_position, HandedLayout.BOTTOM_SIDE);
} finally {
a.recycle();
}
}
}
And then again, in the HandedPathLayout class, the three functions to make sure we get the right kind of LayoutParams:
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof HandedPathLayout.LayoutParams;
}
#Override
public HandedPathLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new HandedPathLayout.LayoutParams(getContext(), attrs);
}
#Override
protected HandedPathLayout.LayoutParams generateDefaultLayoutParams() {
return new HandedPathLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
I've tried this with and without the fully qualified LayoutParams class names.
So, now that's all there and compiles okay, and then in onMeasure of the root ViewGroup, the HandedLayout, I loop through the children and attempt to get at their getLayoutParams() so I can figure out where to put them and how to measure them, and I get this ClassCastException!
05-29 18:16:35.061 9391-9391/com.codesolutions.onehandkeyboard E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.codesolutions.onehandkeyboard, PID: 9391
java.lang.ClassCastException: com.codesolutions.pathlayout.HandedLayout$LayoutParams cannot be cast to com.codesolutions.pathlayout.HandedPathLayout$LayoutParams
at com.codesolutions.pathlayout.HandedLayout.onMeasure(HandedLayout.java:90)
That exception is being thrown from this cast, which seems like it should get the right kind, in the debugger I've confirmed that the child is indeed a HandedPathLayout, and all indications are that the LayoutParams are of the correct type!
HandedPathLayout.LayoutParams lp =
(HandedPathLayout.LayoutParams)child.getLayoutParams();
I just don't get why the LayoutParams are not of the correct type!
And another thing about this I don't get is about the custom View (extends TextView) that I want to put into my HandedPathLayout. It needs a custom layout_rows XML attribute, so do I need to make it it's own LayoutParams? Those are only for ViewGroups, right? But then it's the view that needs that attribute applied, and not the ViewGroup.
UPDATE:
So when I run the debugger and stop just before my cast that fails, in the onMeasure of HandedLayout... I find that, indeed, the LayoutParams objects are NEITHER of the types I expect! The getLayoutParams of the parent (the HandedLayout) returns a FrameLayout.LayoutParams (which makes no sense to me at all) and the child (which is a HandedPathLayout) has a HandedLayout.LayoutParams! WHY?! What am I not understanding here?
Thank you!
I was creating my textview which use as a header in my MainActivity file and everything was okay until I thought I'd better move the code to another class.
While it works and displays the text however when I try to animate it it doesn't work.
The code I was using is in the init() method.
The code is very simple and I'm pasting it here:
public class TextViewHeader extends TextView {
private static final String TAG = "TextViewHeader".toUpperCase();
private Context context;
private FrameLayout frameLayoutWhiteTowerContainer;
private String text;
private int whiteTowerContainerHeight;
private int tower_height;
public TextViewHeader(Context context) {
super(context);
}
public TextViewHeader(Context context, String text, FrameLayout frameLayoutWhiteTowerContainer,
int whiteTowerContainerHeight, int tower_height) {
super(context);
this.context = context;
this.text = text;
this.frameLayoutWhiteTowerContainer = frameLayoutWhiteTowerContainer;
this.whiteTowerContainerHeight = whiteTowerContainerHeight;
this.tower_height = tower_height;
init();
}
public TextViewHeader(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init() {
TextView textView = new TextView(context);
textView.setText(text);
textView.setTextSize(25);
textView.setTextColor(Color.parseColor("#FF545454"));
Typeface typefaceCondensed = Typeface.create("sans-serif-condensed", Typeface.BOLD);
textView.setTypeface(typefaceCondensed);
FrameLayout.LayoutParams tv_header_params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT);
int topMargin = whiteTowerContainerHeight / 2 - tower_height / 2 - 100;
tv_header_params.topMargin = topMargin;
tv_header_params.gravity = Gravity.CENTER_HORIZONTAL;
textView.setLayoutParams(tv_header_params);
frameLayoutWhiteTowerContainer.addView(textView, tv_header_params);
}
}
The code in MainActivity is :
tvHeader = new TextViewHeader(MainActivity.this, "TOWER", flWhiteTowerContainer,
whiteTowerContainerHeight, tower_height ); // create inside onCreate method
tvHeader.animate().y(515).setDuration(1500).start(); //and the animation happens later onMapReady
You are starting an animation before the TextViewHeader is laid out.
Try:
tvHeader = new TextViewHeader(MainActivity.this, "TOWER", flWhiteTowerContainer,
whiteTowerContainerHeight, tower_height );
tvHeader.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
// Layout complete but not drawn yet
tvHeader.getViewTreeObserver().removeOnPreDrawListener(this);
tvHeader.animate().y(515).setDuration(1500).start();
return false;
}
});
The problem is that I'm implementing the TextView but I don't use it. Instead I just create another TextView in the class and I use this instead of my class.
In other words the code :
TextView textView = new TextView(context);
shouldn't exist and just use this to setText, setTextSize, color, etc.
this.setText(text);
this.setTextSize(25);
this.setTextColor(Color.parseColor("#FF545454"));
I would like to inflate a LinearLayout with multiple instances of another LinearLayout. How can I do that? My problem is that I seem to always use the same instance and hence add that instance over and over again.
In short: What I need is a way to add new instances of a LinearLayout child to another LinearLayout parent.
Here is what I have done so far:
private void setupContainers() {
LayoutInflater layoutInflater = (LayoutInflater)this.getSystemService(MainActivity.LAYOUT_INFLATER_SERVICE);
LinearLayout parentContainer = (LinearLayout)this.findViewById(R.id.parent_container);
for (int i = 0; i < someNumber; i++) {
LinearLayout childContainer = (LinearLayout) layoutInflater.inflate(R.layout.child_container, null);
parentContainer.addView(childContainer);
}
}
Try this:
for (int i = 0; i < someNumber; i++) {
LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); // or any other layout params that suit your needs
LinearLayout childContainer = new LinearLayout(this);
parentLayout.addView(childContainer, params)
}
EDIT
Considering you need to use the content from XML, you'll need to create a custom class that extends LinearLayout and initialize in there all its properties. Something like:
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyLinearLayout(Context context) {
super(context);
init(context);
}
private void init(Context context) {
inflate(context, R.id.R.layout.child_container, this);
// setup all your Views from here with calls to getViewById(...);
}
}
Also, since your custom LieanrLayout extends from LinearLayout you can optimize the xml by replacing the root <LinearLayout> element with <merge>. Here is a short documentation and an SO link. So the for loop becomes:
for (int i = 0; i < someNumber; i++) {
LinearLayout.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); // or any other layout params that suit your needs
LinearLayout childContainer = new MyLinearLayout(this);
parentLayout.addView(childContainer, params); // feel free to add or not the LayoutParams object
}
Friends,
I created an UI component "compTV" that extends Textview. It works very well.
Now i want to create an UI compoentent "3compTV" that just consists out of 3 "compTV" ´s next to each other.
The code, creating a LinearLayout and add 3 "compTV" ´s works very well if i just extend Activity.
But how to create a Component out of this?
What class do i have to extend for the "3compTV" component and what else would be necessary.
When i extend compTV only one object will be drawn. So i guess i have to extend a different class or take some other approach to this problem.
Thanks for your support
public class 3compTV extends compTV{
Context ctx;
int layoutMaringLeft = 100;
int layoutMaringRight = 0;
int layoutMaringTop = 0;
int layoutMaringBottom = 0;
int amountOfComponents = 5;
public components(Context context) {
super(context);
ctx = context;
Log.d(ctx.getString(R.string.app_name), "components, Constructor1");
compTV comp1 = new compTV(ctx);
compTV comp2 = new compTV(ctx);
compTV comp3 = new compTV(ctx);
comp2.setLetter("A");
comp2.setState("grey");
comp3.setLetter("A");
comp3.setState("grey");
LinearLayout LL2 = new LinearLayout(ctx);
LL2.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(layoutMaringLeft, layoutMaringTop,
layoutMaringRight, layoutMaringBottom);
LL2.addView(comp1, layoutParams);
comp1.setLetter("H");
comp1.setState("green");
LL2.addView(comp2, layoutParams);
LL2.addView(comp3, layoutParams);
}
public components(Context context, AttributeSet attrs) {
super(context, attrs);
ctx = context;
Log.d(ctx.getString(R.string.app_name), "components, Constructor2");
}
public components(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
ctx = context;
Log.d(ctx.getString(R.string.app_name), "components, Constructor3");
}
}
Make a view that extends LinearLayout and contains 3 instances of compTV.
public class 3CompTV extends LinearLayout {
public 3CompTV(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
for (int i = 0; i < 3; i++) {
addView(new CompTV(context));
}
}
}
My personal preference would be to put the 3 CompTV views in an XML layout, with their parent element being <merge>. This allows you to specify their attributes like wrap_content in XML, which I find much cleaner. You add them to your custom view like this:
public class 3CompTV extends LinearLayout {
public 3CompTV(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(LinearLayout.VERTICAL);
View.inflate(context, R.id.three_comp_tvs, this);
}
}