I am trying to set a custom view to wrap_content in both height and width programatically. The problem is that no matter what i do it is always presented as match_parent.
This is the view
public class Circle extends View
{
public Circle(Context context) {
super(context);
}
public Circle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public Circle(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(0xffff0000);
canvas.drawCircle(mmToPx(4), mmToPx(4), mmToPx(4), paint);
}
public float mmToPx(int mm) {
DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
return mm * displayMetrics.xdpi * (1.0f/25.4f);
}
this is the main code
public void addCircle()
{
Circle circle = new Circle(this);
Random r = new Random();
FrameLayout.LayoutParams cParams = new FrameLayout.LayoutParams(-1, -2);
cParams.setMargins(r.nextInt((int) (w - (mmToPx(diam)))), r.nextInt((int) (h - (mmToPx(diam)))), 0, 0);
circle.setLayoutParams(cParams);
circle.setBackgroundColor(0xff00ff00);
Log.v("sd", circle.getLayoutParams().width + "");
circle.setId(R.id.circle);
circle.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
count++; scoreTv.setText("Score:\n"+count);
frame.removeView(findViewById(R.id.circle));
addCircle();
}
});
frame.addView(circle);
}
Thank you for help :)
First- NEVER use magic numbers like -2. Use ViewGroup.LayoutParams.WRAP_CONTENT. Otherwise your code becomes difficult to read and may contain bugs (for example, if they changed the value).
Second- you aren't actually using cParams anywhere. You want to use the version of addView that takes the view and the layout params. Otherwise how will it know to use those layout params with that view.
Third- you don't have an onMeasure function in your view. Without that, how will it know how big your view is so it can actually wrap content on it? You'll need to define onMeasure so it can get the width and height of your view.
try this:
view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT))
Related
I'm trying to force a drawn view to be in an exact dimension.
No matter how I try it ignores the dimensions.
Code:
My view to be drawn in 250x250 pixels:
public class MyCustomView extends View {
public MyCustomView(Context context) {
super(context);
}
public MyCustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
FlowLayout layout = new FlowLayout(getContext());
for (int i = 0; i < 30; i++) {
TextView textView = new TextView(getContext());
textView.setText("[text#" + i + "]");
textView.setBackgroundColor(Color.GREEN);
layout.addView(textView);
}
int dimension = 250;
layout.measure(
dimension,
dimension);
layout.layout(0, 0,
dimension,
dimension);
layout.draw(canvas);
}
}
Result:
The layout clearly not renders in 250x250 pixels.
If I swap the FlowLayout to a standard Android SDK layout, like to a Linear Layout, the result is the same.
Please help if you can.
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 have added a seekbar to one of my activities.
Its max value is 5. Now, I want to display the divider values (with increment 1, like 0, 1, 2, 3, 4 and 5) below my seekbar. How can I do that?
Is there any system method to achieve this which I am not able to put my hands on? Any inputs are welcomed.
NOTE : I want to apply any changes programatically, not from xml. The numbers should be separated at equal intervals. I could not edit it that precisely though.
I am supposing you want to display view like below in picture.
if that is the case you have to create your own customSeekbar like give code.
CustomSeekBar.java
public class CustomSeekBar extends SeekBar {
private Paint textPaint;
private Rect textBounds = new Rect();
private String text = "";
public CustomSeekBar(Context context) {
super(context);
textPaint = new Paint();
textPaint.setColor(Color.WHITE);
}
public CustomSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
textPaint = new Paint();
textPaint.setTypeface(Typeface.SANS_SERIF);
textPaint.setColor(Color.BLACK);
}
public CustomSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
textPaint = new Paint();
textPaint.setColor(Color.BLACK);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
// First draw the regular progress bar, then custom draw our text
super.onDraw(canvas);
int progress = getProgress();
text = progress + "";
// Now get size of seek bar.
float width = getWidth();
float height = getHeight();
// Set text size.
textPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
textPaint.setTextSize(40);
// Get size of text.
textPaint.getTextBounds(text, 0, text.length(), textBounds);
// Calculate where to start printing text.
float position = (width / getMax()) * getProgress();
// Get start and end points of where text will be printed.
float textXStart = position - textBounds.centerX();
float textXEnd = position + textBounds.centerX();
// Check does not start drawing outside seek bar.
if (textXStart <= 1) textXStart = 20;
if (textXEnd > width) {
textXStart -= (textXEnd - width + 30);
}
// Calculate y text print position.
float yPosition = height;
canvas.drawText(text, textXStart, yPosition, textPaint);
}
public synchronized void setTextColor(int color) {
super.drawableStateChanged();
textPaint.setColor(color);
drawableStateChanged();
}
}
In your Xml file use your custom file like below
<com.waleedsarwar.customseekbar.CustomSeekBar
android:id="#+id/seekbar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="5"
android:paddingBottom="16dp" />
This is another approach. I am extending a linearlayout. I put seekbar and another linearlayout(layout_5) which contains 6 textviews with 0-1-2-3-4-5. Better option would be creating a dynamic image(get width from seekBar) which has these numbers according to segment count.
I force seekbar's indicator to stop at specific points(6 points in your case). Instead of doing this, it is possible to set seekBar's maximum progress value to 5. It will work, but it will not give a good user experience.
public class SegmentedSeekBar extends LinearLayout {
private int[] preDefinedValues;
private int currentProgressIndex;
private SeekBar seekBar;
private int segmentCount = 5:
public SegmentedSeekBar(Context context) {
this(context, null, 0);
}
public SegmentedSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.seekBarStyle);
}
public SegmentedSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.SegmentedSeekBar,
0, 0);
try {
segmentCount =
a.getInt(R.styleable.SegmentedSeekBar_segmentCount, -1);
} finally {
a.recycle();
}
init();
}
public void init() {
//this values will be used when you need to set progress
preDefinedValues = new int[segmentCount];
for(int i = 0; i < preDefinedValues.length; i++) {
preDefinedValues[i] = (100/(segmentCount-1)) * i;
}
//Get layout_5
//which is linearlayout with 6 textviews
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View sliderView = inflater.inflate(
getSliderId(segmentCount), null);
//seekbar already inside the linearlayout
seekBar = (SeekBar)sliderView.findViewById(R.id.seek_bar);
//linear layout is vertically align
//so add your 6 textview linearlayout
addView(sliderView);
seekBar.setOnTouchListener(seekBarTouchListener);
}
private int getSliderId(int size) {
return R.layout.layout_5;
}
//this method sets progress which is seen in UI not actual progress
//It uses the conversion that we did in beginning
public synchronized void setProgress(int progress) {
if(preDefinedValues != null && progress < preDefinedValues.length && progress >= 0) {
seekBar.setProgress(preDefinedValues[progress]);
currentProgressIndex = progress;
}
}
//this listener make sure the right progress is seen in ui
//take action when user finish with changing progress
SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
int index = 0;
for(int i = 0; i < preDefinedValues.length; i++) {
//try to find closest preDefinedvalues by comparing with latest value
if(Math.abs(seekBar.getProgress() - preDefinedValues[i]) < Math.abs(seekBar.getProgress() - preDefinedValues[index])) {
index = i;
}
}
setProgress(index);
}
};
}
I have a class MyLayout extending RelativeLayout which includes View type field. MyLayout object is created in xml layout file, so all properties are set there. I need to programatically set size of View field which depends on size of it's parent (MyLayout).
I was trying to set it in constructor, but when I try to use getWidth() method, it returns 0, so I assume that the size is not yet set inside a constructor. I was also trying to set it in onDraw() method, but when I run an application, this internal View is displayed for like second with it's default size and after that time it's scaled to the right size. Then I tried putting it inside onMeasure() method, but this one is called a few times, so again it doesn't seem to be efficient at all.
So what could be the best place to set it?
This is my class:
public class MyLayout extends RelativeLayout {
private View pointer;
public MyLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public MyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyLayout(Context context) {
super(context);
init(context);
}
private void init(Context c) {
pointer = new View(c);
pointer.setBackgroundResource(R.drawable.pointer);
addView(pointer);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)pointer.getLayoutParams();
lp.height = (int)(getHeight() * 0.198);
lp.width = (int)(getWidth() * 0.198);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
in your MyLayout class, override onSizeChanged():
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)pointer.getLayoutParams();
lp.height = (int)(getHeight() * 0.198);
lp.width = (int)(getWidth() * 0.198);
};
I facing problem to draw rectangle at subclass of my android custom view class. Each time super class onDraw method works.But subclass onDraw method never executed. Super class will draw a rectangle and subclass will draw 4 rectangle within the super-class drawn rectangle.I can't fixed this problem.please help me.
Here is my sample code.
SuperClass:
public class ColorFanView extends View{
public ShapeDrawable[] mDrawables;
public ColorFanView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ColorFanView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ColorFanView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvasObject) {
super.onDraw(canvasObject);
int x = 100;
int y = 100;
int width = 80;
int height = 200;
Paint thePaint = new Paint();
thePaint.setColor(Color.WHITE);
RectF rectnagle1 = new RectF(x,y,x+width,y+height);
canvasObject.drawRoundRect(rectnagle1, 10.0f, 10.0f, thePaint);
}
}
Subclass:
public class ColorFanStack extends ColorFanView{
public ColorFanStack(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}
public ColorFanStack(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public ColorFanStack(Context context) {
super(context);
initView();
}
public void initView() {
mDrawables = new ShapeDrawable[4];
float[] outerR1 = new float[] { 12, 12, 12, 12, 0, 0, 0, 0 };
mDrawables[0] = new ShapeDrawable(new RoundRectShape(outerR1, null, null));
mDrawables[0].getPaint().setColor(Color.RED);
mDrawables[1] = new ShapeDrawable(new RectShape());
mDrawables[1].getPaint().setColor(Color.WHITE);
mDrawables[2] = new ShapeDrawable(new RectShape());
mDrawables[2].getPaint().setColor(Color.BLUE);
mDrawables[3] = new ShapeDrawable(new RectShape());
mDrawables[3].getPaint().setColor(Color.YELLOW);
}
#Override
protected void onDraw(Canvas canvasObj) {
super.onDraw(canvasObj);
int x = 100;
int y = 100;
int width = 80;
int height = 40;
int canvasSpace =5;
for (Drawable dr : mDrawables) {
dr.setBounds(x, y, x + width, y + height);
dr.draw(canvasObj);
y += height + canvasSpace;
}
}
}
XML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/myViewGroup" android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<com.test.colorfan.ColorFanView
android:layout_width="200dip" android:layout_height="400dip"
android:id="#+id/firstView" />
</RelativeLayout>
Please help me regarding this issue. Hopefully, I will get a reply soon.
My guess is that your layout (please edit the question to include your layout), is defining your ColorFanView instances in such a way that they have 0 height or width; therefore, the parent View does not draw them.
EDIT 7/27/2011: Habibur Rahman added his layout XML to the question. This is the new answer:
Your two classes work, but you added the wrong one to your layout (you should have used ColorFanStack instead of ColorFanView). An instance of ColorFanStack will inherit the drawing of ColorFanView (by virtue of the fact that your ColorFanStack.onDraw() method calls super.onDraw()). I think that that was the behavior that you were trying to achieve.
Here is the XML that I used with the classes as you defined them:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<com.habecker.demo.ColorFanStack
android:layout_width="200dip" android:layout_height="400dip"
android:id="#+id/secondView" />
</RelativeLayout>