How can I set margins in class extends from RelativeLayout?
I have tried this but it doesn't work:
public class MyRelativeLayout extends RelativeLayout {
int margin = 50;
public MyRelativeLayout(Context context) {
super(context);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setLook();
}
private void setLook() {
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(margin, margin, margin, margin);
setLayoutParams(params);
}
}
How should I do this?
Update:
Usage of this view:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<info.korzeniowski.widget.MyRelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" >
//content
</info.korzeniowski.widget.MyRelativeLayout>
</ScrollView>
A custom View should NEVER define its own margins. Margins are purely used for layouting and you cannot reliably use them to design your custom View.
You can essentially replicate the effect margins have without any of the problems that come with margins by using paddings and a child View:
public class MyRelativeLayout extends RelativeLayout {
private final int padding;
public MyRelativeLayout(Context context) {
super(context);
// Use 50 dip instead of 50 pixels
this.padding = LayoutHelper.dpToPixel(context, 50);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// Use 50 dip instead of 50 pixels
this.padding = LayoutHelper.dpToPixel(context, 50);
setLook();
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Use 50 dip instead of 50 pixels
this.padding = LayoutHelper.dpToPixel(context, 50);
setLook();
}
private void setLook() {
setPadding(this.padding, this.padding, this.padding, this.padding);
final View innerView = ...;
final LayoutParams innerViewParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addView(innerView, innerViewParams);
}
}
The View called innerView should contain all the content you want to display in your custom View.
Related
I want to create a custom View, which shows a Card with follwing Contents:
TextView (Caption)
TextView (Description)
LinearLayout (innerLayout)
So i just extended a LinearLayout and inflated my Layout file with it:
public class FrageContainerView extends LinearLayout {
private TextView objTextViewCaption;
private TextView objTextViewDescription;
private String caption;
private String description;
private LinearLayout objLayoutInner;
public FrageContainerView(Context context) {
this(context, null);
}
public FrageContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.options_frageContainerView, 0, 0);
caption = a.getString(R.styleable.options_frageContainerView_caption);
description = a.getString(R.styleable.options_frageContainerView_description);
a.recycle();
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.view_fragecontainer, this, true);
objLayoutInner = (LinearLayout) findViewById(R.id.linearlayout_inner);
objTextViewCaption = (TextView) findViewById(R.id.textview_caption);
objTextViewDescription = (TextView) findViewById(R.id.textview_description);
objTextViewCaption.setText(caption);
objTextViewDescription.setText(description);
}
A user which uses my custom View should be able to add his own Components preferably inside the XML like this:
<FrageContainerView
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:caption="Hallo"
custom:description="LOLOLOL"
android:background="#FF00FF00">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="sdsdfsdf"/>
</FrageContainerView>
The current State is, that the defined EditText's are inflated inside my custom View. I want the EditTexts in my Example to be added to the InnerLayout instead to append them to Custom View.
What's the best approach to do this?
The essence of this problem is how to add child Views to a GroupView that is itself a child of the custom layout.
This is relatively straightforward programmatically, but more of an issue in XML.
Androids LayoutInflater logically interprets the nested levels in an XML file and builds the same structure in the hierarchy of Views it creates.
Your example XML defines 4 EditText Views as first tier children of FrageContainerView, but you want them to be created as second tier children of FrageContainerView sitting inside your LinearLayout. This would mean changing Androids LayoutInflater which is a core component of the whole Android system.
To do this programmatically you could do something like the following:
public class FrageContainerView extends LinearLayout {
private TextView objTextViewCaption;
private TextView objTextViewDescription;
private String caption;
private String description;
private LinearLayout objLayoutInner;
public FrageContainerView(Context context) {
this(context, null);
}
public FrageContainerView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FrageContainerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context, attrs);
}
private void initialize(Context context, AttributeSet attrs) {
// Create your 3 predefined first tier children
// Create the Caption View
objTextViewCaption = new TextView(context);
// You can add your new Views to this LinearLayout
this.addView(objTextViewCaption)
// Create the Description View
objTextViewDescription = new TextView(context);
// You can also provide LayoutParams when you add any of your new Views if you want to
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
this.addView(objTextViewDescription, params);
// Create your inner LinearLayout
objLayoutInner = new LinearLayout(context);
objLayoutInner.setOrientation(VERTICAL);
// Add it
this.addView(objLayoutInner);
TypedArray a =
context.obtainStyledAttributes(attrs, R.styleable.options_frageContainerView, 0, 0);
caption = a.getString(R.styleable.options_frageContainerView_caption);
description = a.getString(R.styleable.options_frageContainerView_description);
a.recycle();
setOrientation(LinearLayout.HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
/**
* Oops! Only just spotted you're inflating your three predefined views
* here. It's fine to do this instead of programmatically adding them as I
* have above. Obviously they should only be added once, so I've commented out
* your version for the moment.
**/
// LayoutInflater inflater =
// (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// inflater.inflate(R.layout.view_fragecontainer, this, true);
//
// objLayoutInner = (LinearLayout) findViewById(R.id.linearlayout_inner);
// objTextViewCaption = (TextView) findViewById(R.id.textview_caption);
// objTextViewDescription = (TextView) findViewById(R.id.textview_description);
objTextViewCaption.setText(caption);
objTextViewDescription.setText(description);
}
}
/** Public method for adding new views to the inner LinearLayout **/
public void addInnerView(View view) {
objLayoutInner.addView(view);
}
/** Public method for adding new views to the inner LinearLayout with LayoutParams **/
public void addInnerView(View view, LayoutParams params) {
objLayoutInner.addView(view, params);
}
You would use this in your code with something like the following:
FrageContainerView fragContainerView = (FrageContainerView) findViewById(R.id.my_frag_container_view);
TextView newView = new TextView(context);
newView.setText("whatever");
fragContainerView.addInnerView(newView);
The other thing you could do is
1) Cache all current children and remove them all
ArrayList<View> nestedViews = ViewUtil.getAllChildren(this); removeAllViews();
2) Inflate the layout you have which already contains things
View myLayout = LayoutInflater.from(getContext()).inflate(R.layout.my_layout_with_stuff, null);
3) Add the cached views to the newly inflated layout and add it to the root back
for (View view : nestedViews) {
myLayout.<ViewGroup>findViewById(R.id.contentLayout).addView(view);
}
addView(myLayout);
I've created working custom view for imageView to be able scale down and up by percentages according to diplay screen dimension. In preview tab in AndroidStudio I do not see any errors, preview is working. However my custom view is not changing. When I set to 10% of total width which is .1 my custom view is not changing in preview. When I start my app, my custom view is working fine. I am not sure what am I doing wrong. I will be glad for tip. Thank you.
Here is my xml class:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#drawable/logo"
android:layout_centerInParent="true" />
<com.example.widgets.ImageViewWidthFix
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="#drawable/blue_logo"
android:layout_centerInParent="true"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
app:percentageWidth=".3" />
</RelativeLayout>
Here is my java class:
public class ImageViewWidthFix extends ImageView {
private float percentageWidth;
private float percentageHeight;
public ImageViewWidthFix(Context context) {
super(context);
init(context, null);
}
public ImageViewWidthFix(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public ImageViewWidthFix(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ImageViewWidthFix(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ViewGroup.LayoutParams layoutParams = getLayoutParams();
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
layoutParams.width = (int) (percentageWidth == 1 ? layoutParams.width : displayMetrics.widthPixels * percentageWidth);
layoutParams.height = (int) (percentageHeight == 1 ? layoutParams.height : displayMetrics.heightPixels * percentageHeight);
setLayoutParams(layoutParams);
invalidate();
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ImageViewFixedDimension);
percentageWidth = a.getFloat(R.styleable.ImageViewFixedDimension_percentageWidth, 1);
percentageHeight = a.getFloat(R.styleable.ImageViewFixedDimension_percentageHeight, 1);
a.recycle();
}
}
}
You aren't calling setMeasuredDimension with the new dimensions of the view. That's going to cause problems. From the docs:
CONTRACT: When overriding this method, you must call
setMeasuredDimension(int, int) to store the measured width and height
of this view. Failure to do so will trigger an IllegalStateException,
thrown by measure(int, int). Calling the superclass' onMeasure(int,
int) is a valid use.
While you're not getting an exception because the superclass is calling it, you aren't correctly setting them either which will cause problems elsewhere.
I am extending a frame layout
and when in the constructor (after it loaded the view from xml) i call getChildCount()
I get 0. How to fix this ?
public class DisabledFrameLayout extends FrameLayout {
public DisabledFrameLayout(Context context) {
super(context);
init(context, null,0);
}
public DisabledFrameLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context,attrs,defStyle);
}
public DisabledFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context,attrs,0);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DisabledFrameLayout, 0, 0);
if(a.hasValue(R.styleable.DisabledFrameLayout_disable_descendants)) {
boolean disable = a.getBoolean(R.styleable.DisabledFrameLayout_disable_descendants, false);
if(disable) {
disableDescendants(this);
}
}
a.recycle();
}
private void disableDescendants(ViewGroup v) {
for (int i=0; i<v.getChildCount();i++) {
if(v.getChildAt(i) instanceof ViewGroup) {
disableDescendants((ViewGroup)v.getChildAt(i));
}
v.setEnabled(false);
v.setFocusable(false);
v.setFocusableInTouchMode(false);
}
}
}
xml
<com.lenabru.DisabledFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:disable_descendants="true"
>
<include
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
layout="#layout/fragment_p" />
</com.lenabru.DisabledFrameLayout>
In state of constructing your FrameLayout, children are still not fully attached to the view. So, calling getChildCount() will return you 0.
If you want to iterate over child views and update them, do it inside onLayout() or onMeasure().
Refs:
http://blog.denevell.org/android-custom-views-onlayout-onmeasure.html
ViewGroup - check this example code.
I need to create a custom view, which extends RelativeLayout and simply needs to have and imageView as in the same size of this customView.
My code is:
public class MyCustomButton extends RelativeLayout {
ImageView buttonCoverImage;
public MyCustomButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyCustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyCustomButton(Context context) {
super(context);
setClickable(true);
setFocusable(true);
setEnabled(true);
buttonCoverImage = new ImageView(getContext());
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
setLayoutParams(lp);
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
buttonCoverImage.setLayoutParams(new RelativeLayout.LayoutParams(100, 100));
buttonCoverImage.setBackgroundResource(R.drawable.button_selector);
buttonCoverImage.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
performClick();
}
});
this.addView(buttonCoverImage);
}
}
And in xml, i created this view like:
The problem is i can't see the buttonCoverImage? Somehow it's not been created, or added to myCustomView... What could the problem be?
If you add View by xml, then android will implement constructor
public MyCustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
And in your case it's empty. I think it's your problem.
I want to do a custom horizontal navigation bar, which consists of as many dots as i have pages.
My thoughts are:
Create a Custom ListView
Create Class Dot extends from View
Add all these dots to the custom listview dynamically..
Is this right to do so?
EDIT:
public class NavigationBarLesson extends LinearLayout {
private LessonConfig config = LessonConfig.getInstance();
private ArrayList<NavigationCircle> navigationCircles;
private int pageCount;
public NavigationBarLesson(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public NavigationBarLesson(Context context) {
super(context);
init(context);
}
private void init(Context context) {
Log.i("init","yes");
pageCount = config.getLektionCount();
navigationCircles = new ArrayList<NavigationCircle>();
for(int i=0; i < pageCount; i++){
this.addView(new NavigationCircle(context));
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = View.MeasureSpec.getSize(widthMeasureSpec);
int height = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
}
public class NavigationCircle extends ImageView{
private static Bitmap img;
private Bitmap activeImg;
public NavigationCircle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public NavigationCircle(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public NavigationCircle(Context context) {
super(context);
init();
}
public void init() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(0, 0, 5, 0);
this.setLayoutParams(lp);
this.setBackgroundResource(R.drawable.upcoming_pages);
}
public Bitmap getImg() {
return img;
}
public Bitmap getActiveImg() {
return this.activeImg;
}
}
Well, I do not see why you would need a ListView. Create a horizontal LinearLayout with some simple views like TextView's or ImageView's etc added dynamically to the LinearLayout.