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.
Related
I have a custom RelativeLayout and I inflate an xml res file in it.
This works fine if I use the custom layout in an xml file and set it as contentview, but if I try to add it in the code with new LocationItem(this) and addChild() the findViewById method always returns null in the constructor of the custom RelativeLayout.
Here is the code:
public class LocationItem extends RelativeLayout {
private String parcelType;
private int countIntoBox, countFromBox;
private RelativeLayout deliveryContainer, pickupContainer;
private TextView countPickup, countDelivery;
public LocationItem(Context context) {
this(context, null);
}
public LocationItem(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LocationItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
inflate(getContext(), R.layout.list_item_location, this);
deliveryContainer = (RelativeLayout) findViewById(R.id.rl_location_delivery_container);
pickupContainer = (RelativeLayout) findViewById(R.id.rl_location_pickup_container);
countPickup = (TextView) findViewById(R.id.tv_location_pickup_count);
countDelivery = (TextView) findViewById(R.id.tv_location_delivery_count);
countPickup.setOnClickListener(getShowNumberPickerListener());
countDelivery.setOnClickListener(getShowNumberPickerListener());
}
private OnClickListener getShowNumberPickerListener() {
return new OnClickListener() {
#Override
public void onClick(View view) {
showNumberPickerDialog(view);
}
};
} ...
}
Add custom view in activity
mRootLayoutLocations.addView(new LocationItem(this));
The view is inflated correctly, because I can see it, but when I try to access a view inside the custom view the app crashes with a NullPointerException.
Ok i inflated the view into a View(holder)
View v = inflate(getContext(), R.layout.list_item_location, this);
and then access the views via v.findViewById. Now it's working.
Code:
View v = inflate(getContext(), R.layout.list_item_location, this);
deliveryContainer = (RelativeLayout) v.findViewById(R.id.rl_location_delivery_container);
pickupContainer = (RelativeLayout) v.findViewById(R.id.rl_location_pickup_container);
countPickup = (TextView) v.findViewById(R.id.tv_location_pickup_count);
countDelivery = (TextView) v.findViewById(R.id.tv_location_delivery_count);
countPickup.setOnClickListener(getShowNumberPickerListener());
countDelivery.setOnClickListener(getShowNumberPickerListener());
You need to be using appropriate constructors, not overloading them.
public class LocationItem extends RelativeLayout {
private String parcelType;
private int countIntoBox, countFromBox;
private RelativeLayout deliveryContainer, pickupContainer;
private TextView countPickup, countDelivery;
public LocationItem(Context context) {
super(context);
init(context, null, 0);
}
public LocationItem(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public LocationItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
inflate(getContext(), R.layout.list_item_location, this);
deliveryContainer = (RelativeLayout) findViewById(R.id.rl_location_delivery_container);
pickupContainer = (RelativeLayout) findViewById(R.id.rl_location_pickup_container);
countPickup = (TextView) findViewById(R.id.tv_location_pickup_count);
countDelivery = (TextView) findViewById(R.id.tv_location_delivery_count);
countPickup.setOnClickListener(getShowNumberPickerListener());
countDelivery.setOnClickListener(getShowNumberPickerListener());
}
private OnClickListener getShowNumberPickerListener() {
return new OnClickListener() {
#Override
public void onClick(View view) {
showNumberPickerDialog(view);
}
};
}
...
}
I fear you must inflate the view instead of finding it from nowhere. The method
findindViewById(int Id)
must be called within the Actvity's onCreate or with a view within which the child view/widget you are trying to find is resides in.
If all of the child views resides in a single xml file(within single parent root)
View rootView=(View) LayoutInflater.from(context).inflate(R.layout.list_item_location);
pickupContainer = (RelativeLayout) rootview.findViewById(R.id.rl_location_pickup_container);
you can try this. init views in the method onFinishInflate().This method will be called called as the last phase of inflation, after all child views have been added.So you can avoid NPE.
this should work
public LocationItem(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this = inflate(getContext(), R.layout.list_item_location,null);
...
i have the following code, and the problem, that my function rlposition() isn't available from outside the class.
public class RLbadge extends TextView {
public RLbadge(Context context) {
super(context);
this.setTypeface(null, Typeface.BOLD);
this.setTextColor(Color.WHITE);
this.setBackgroundResource(R.drawable.badge);
this.setTextSize(18);
}
public void rlposition(Button pButton) {
// THIS FUNCTION ISNT SEEN FROM OUTSIDE WHY?
}
protected void onDraw (Canvas canvas) {
super.onDraw(canvas);
}
}
Why is the function rlposition not visible from outside of the class?
Isn't it possible to add functionality to an extended TextView?
<YOURPACKAGENAME.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="32sp"
android:text="TEASTING" />
Here is Class
public class MyTextView extends TextView {
public MyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public MyTextView(Context context) {
super(context);
init(null);
}
private void init(AttributeSet attrs) {
// Do your staff
}
}
}
i now answered the question for myself and put it here maybe somebody needs the answer in the future.
The problem was this line of calling:
TextView badgeInfoscan = new RLbadge(this);
badgeInfoscan.rlposition(); // here the error comes
changed to
RLbadge badgeInfoscan = new RLbadge(this);
badgeInfoscan.rlposition(); // the function is visible
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.
Using the following code:
public class CustomView extends RelativeLayout {
public CustomView(Context context) {
this(context, null, 0);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
inflate(getContext(), R.layout.custom_view, this);
}
}
The layout is simple:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/custom_view_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f00"/>
The hierarchyviewer shows the following:
The CustomView hierarchy is useless and I would like to remove it.
Is there a way to create a custom view extending a ViewGroup without adding that additional View?
If your CustomView is already a RelativeLayout in you XML layout you can just delete the RelativeLayout with "#+id/custom_view_id" and use as father the tag
That will merge the children with the CustomView without using an extra RelativeLayout.
The CustomView will setBackgroundColor
public class CustomView extends RelativeLayout {
public CustomView(Context context) {
this(context, null, 0);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
private void initView() {
setBackgroundColor(Color.parseColor("#f00"));
inflate(getContext(), R.layout.custom_view, this);
}}
and the layout xml file will be:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<!-- children here -->
</merge>
I'm trying something like this
public class CustomViewSubclass extends HorizontalScrollView{
private LinearLayout layout;
public CustomViewSubclass(Context context) {
this(context,null,0);
}
public CustomViewSubclass(Context context, AttributeSet attrs) {
this(context,attr,0);
}
public CustomViewSubclass(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
layout = new LinearLayout(context);
}
// This is called from the `Activity`
public void startAsyncTask() { // code }
// This method is called in the `onPostExecute()` of an `AsyncTask` subclass
public void doSomething(Context context) {
ImageView image = ImageView(context);
layout.addView(image); // NullPointerException here, layout seems to be null
}
but it seems that layout on doSomething() is null. How is that even possible? I'm initializing it on the constructor... and I never re-initialize it again;
I'm adding my custom view via XML
<com.mypackage.CustomViewSubclass
android:layout_width="wrap_content"
android:layout_width="match_parent" />
Ok I fixed it, it was an stupid mistake made by me:
I used super() on the 3 methods, instead of using this().
public CustomViewSubclass(Context context) {
super(context,null,0);
}
public CustomViewSubclass(Context context, AttributeSet attrs) {
super(context,attr,0);
}
public CustomViewSubclass(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
layout = new LinearLayout(context);
}
Solution:
public CustomViewSubclass(Context context) {
this(context,null,0);
}
public CustomViewSubclass(Context context, AttributeSet attrs) {
this(context,attr,0);
}
public CustomViewSubclass(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
layout = new LinearLayout(context);
}