Custom Layout that's compatible with Android Studio's drag editing? - android

For a simplest example, with the (deprecated) AbsoluteLayout, you can freely drag the views around. But this following code wont let you:
public class TestLayout extends AbsoluteLayout {
public TestLayout(#NonNull Context context) {
super(context);
}
}
defining an additional inner class LayoutParams also wont help much
public static class LayoutParams extends AbsoluteLayout.LayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height, int x, int y) {
super(width, height,x, y);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
}
GIF:
cannot drag-edit a subclass of AbsoluteLayout
So how do I make a custom layout that's friendly to Android Studio's drag-editing mechanism?
Any help will be appreciated.

Related

android custom layout parameters ClassCastException and what about the View?

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!

Can't set view params to WRAP_CONTENT programatically

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))

Click listener for drawable inside custome framelaout class android

hi I am developing android application in which i am using one custom frame layout class. Inside that class I am using one drawable and with the help of canvas i m drawing that. I did this in following way :
public class BackgroundContainer extends FrameLayout implements OnTouchListener{
Drawable mShadowedBackground;
public BackgroundContainer(Context context) {
super(context);
init();
}
public BackgroundContainer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public BackgroundContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mShadowedBackground =
getContext().getResources().getDrawable(R.drawable.actionbar);
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
Log.i("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", "OOOOOOOOOOOOOOOOOOOOOO");
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN: {
invalidate();
}
}
return true;
}
#Override
protected void onDraw(Canvas canvas) {
mShadowedBackground.setBounds(getWidth()-150, 0, getWidth(), mOpenAreaHeight);
canvas.save();
canvas.translate(0, mOpenAreaTop);
mShadowedBackground.draw(canvas);
canvas.restore();
}
}
}
Now I am want to listen click even on my drawable. I implement ontouch event but its not working. Am i doing it in wrong way. Need help thank you.
Drawables are not clickable as it is not considered as a view.
Like deepak already said: by implementing the corresponding listener you just provide the behavior what should happen for a specific event. You still need to add the listener :) In your case this would help (in your init()):
setOnTouchListener(this);

How to create a custom LayoutParams to be used on a custom layout?

I'm fairly proficient at creating complex custom layouts based on ViewGroup. The only thing I'm missing is the ability to create my custom LayoutParams. I really need the ability to get the margins and why not create other extra params to pass in to the parent.
How can I go about creating a custom LayoutParam and using it via xml? I tried using a LinearLayout.LayoutParam but it's obviously crashing since the parent is not a LinearLayout. How can I work with LayoutParams on custom layouts?
Update:
As of now I'm sticking with using a FrameLayout and overriding the onMeasure and onLayout functions to do the layout myself. This does provide FrameLayout.LayoutParams. I'm guessing the childs would have to support the custom LayoutParam?
In your custom layout, create a nested class extending ViewGroup.LayoutParams. Then override some methods (all of the required ones are in my example). Here's a stripped-down version of one of my custom layouts:
public class MyLayout extends ViewGroup {
public MyLayout(Context context) {
}
public MyLayout(Context context, AttributeSet attrs) {
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
}
#Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
#Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams();
}
#Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
#Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return generateDefaultLayoutParams(); // TODO Change this?
}
public static class LayoutParams extends ViewGroup.LayoutParams {
public LayoutParams() {
}
public LayoutParams(int width, int height) {
}
public LayoutParams(Context context, AttributeSet attrs) {
}
}
}
Further explanation: How to create a FlowLayout (thanks for the link Luksprog!)

how do you color percentage of a textview in android?

I know Spannable can help me color any specific letters in a textview. However, is it possible to color 1/2 or 1/3 of a letter. I wanted to color text within a textview by percentage instead of by letter. Thanks for reading, and please let me know if you had some idea or solution to this.
thanks
It may be easier to use Spanned thought android.text.HTML
So something like this:
Spanned text = HTML.fromHtml("Sp<span style=\"color:red\">ann</span>able");
textView.setText(text);
It can also be used to add images, but it a bit more complicated.
UPDATE
I re-read your question and thought of a solution. You could create a custom view that has two textViews in a FrameLayout (on top of each other) and then resize the one on the top relative to the percentage. Something like this:
import android.content.Context;
import android.content.res.ColorStateList;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import android.widget.TextView;
public class ProgressTextView extends FrameLayout {
private TextView backgroundTextView;
private TextView foregroundTextView;
private CharSequence text;
private ProgressTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private ProgressTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private ProgressTextView(Context context) {
super(context);
init(context, null);
}
private void init(Context context, AttributeSet attrs) {
backgroundTextView = new TextView(context);
foregroundTextView = new TextView(context);
addView(backgroundTextView);
addView(foregroundTextView);
// process custom attributeSet from xml to set the colours
}
public void setBackgroundTextColor(int color) {
backgroundTextView.setTextColor(color);
}
public void setBackgroundTextColor(ColorStateList colors) {
backgroundTextView.setTextColor(colors);
}
public void setForegroundTextColor(int color) {
backgroundTextView.setTextColor(color);
}
public void setForegroundTextColor(ColorStateList colors) {
backgroundTextView.setTextColor(colors);
}
public void setPercentage(float per) {
foregroundTextView.setWidth((((float) backgroundTextView.getWidth()) / 100f) * per);
}
public void setText(CharSequence text) {
this.text = text;
backgroundTextView.setText(text);
foregroundTextView.setText(text);
}
public CharSequence getText() {
return text;
}
}
PS: not tested ;) just an idea
MORE UPDATE
Apparently, with this method the text gets shortened instead of just cropped as I expected. The maybe on creation of the foregroundTextView you could do this:
foregroundTextView = new TextView(context) {
#Override
protected void onDraw (Canvas canvas) {
super.onDraw(canvas);
Rect bounds = canvas.getClipBounds();
bounds.right (((float) bounds.right) / 100.0f) * per;
canvas.clipRect(bounds);
}
};
And also add the per variable and modify setPercentage(float per) to be just a setter:
private float per = 0.0f;
public void setPercentage(float per) {
this.per = per;
}
Hope this one works ;)
You could draw the letters yourself on a canvas. Each letter consisting of a bunch of interconnected arcs, that way you could style each individually.
Having to draw each letter would be considerably painstaking though, e.g., paint.setColor(), canvas.drawArc(), paint.setColor(), canvas.drawArc(), and so on...
create a drawable and override it's onDraw method and paint canvas how you wish and set it as background to your textview

Categories

Resources