Edit: This problem was down to me passing the wrong view to the Touch Delegate...problem is resolved now....
My RelativeLayout extension enlarges the clickable area of an ImageView. The RelativeLayout is the root View of the ListView row. The problem is when tapping areas that should be delegated to the ImageView, the ListView's onItemClicked is triggered.
My code is at the end and below is an image of what the extended bounds of the ImageView should be. If I tap on blue areas that are not the image, the click is received by the ListView.
public class DelegatingRelativeLayout extends RelativeLayout {
//relevant code moved to top
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
final float density = getContext().getResources().getDisplayMetrics().density;
int extra = (int) (density * 4 + 0.5f);
Rect rect = new Rect(0, 0, mEnlargedView.getWidth(), getHeight());
rect.right += extra; //extend bounds by 4 pixels
setTouchDelegate(new TouchDelegate(rect, mEnlargedView));
mExtendedBounds = rect;
}
public void setEnlargedView(View v) {
mEnlargedView = v;
}
private static final boolean DEBUG_DRAW = true;
private View mEnlargedView;
private Rect mExtendedBounds;
//constructors removed
#Override
protected void dispatchDraw(Canvas canvas) {
if (DEBUG_DRAW) {
Paint p = new Paint();
p.setColor(Color.BLUE);
canvas.drawRect(mExtendedBounds, p);
}
super.dispatchDraw(canvas);
}
}
Ugh, I've just realized I've been passing the wrong view to the touch delegate >.< It's been working fine now.... >.<
Related
I have to make a gradient color effect in the view that generated dynamically according to the scenario and also the view would be of any shape (diagonal or square)
As shown in image, gradient effect could be in any shape.
Also, if I create custom view for every possible case and play with Visibility, then how I will manage these views to fit perfectly on every device screen size?
Just need a small help to start.
Thanks in advance.
I have solved the issue using this approach.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CustomView(this));
}
}
and now create a CustomView class
public class CustomView extends View {
Rect rect;
private Rect rectangle;
private Paint paint;
public CustomView(Context context) {
super(context);
int x = 50;
int y = 50;
int sideLength = 200;
int sideLength1 = 100;
rectangle = new Rect(x, y, sideLength, sideLength1);
paint = new Paint();
paint.setColor(Color.GRAY);
}
#Override
protected void onDraw(Canvas canvas) {
int width = this.getMeasuredWidth();
int height = this.getMeasuredHeight();
BitmapShader shader;
//shader = new BitmapShader(header, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//paint.setShader(shader);
Path path = new Path();
path.moveTo(40,40);
path.lineTo(5,height/2);
path.lineTo(width/2,height/4);
path.lineTo(width/2,0);
canvas.drawPath(path,paint);
}
I am trying to create a custom drag shadow with text inside a small colored rectangle. Any help will be highly appreciated. Tried this in 2 ways:
a. Created a custom layout with a TextView and inflated it like this:
public void onItemLongClick(View view, int position) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view1 = inflater.inflate(R.layout.drag_shadow, null);
TextView textCount = (TextView) view1.findViewById(R.id.textCount);
textCount .setText("" + count);
View.DragShadowBuilder shadowBuilder = new MyDragShadowBuilder(view1); // Tried with both view1 and textCount
view.startDrag(data, shadowBuilder, view1, 0);
}
Problem with this approach is since view1 is not added to view hierarchy, inside MyDragShadowBuilder, getView().getWidth() returns 0 and hence, nothing is drawn
b. Tried creating a custom TextDrawable class which extends Drawable. This showed the text but its very small though the font size i gave is 20sp.
TextDrawable class:
public class TextDrawable extends Drawable {
private final String text;
private final Paint paint;
public TextDrawable(Context context,String text) {
this.text = text;
int size = context.getResources().getDimensionPixelSize(R.dimen.drag_shadow_font); //20sp
Logger.log("TAG","Size="+size); //60
this.paint = new Paint();
paint.setColor(Color.BLUE);
paint.setTextSize(size);
paint.setAntiAlias(true);
// paint.setFakeBoldText(true);
// paint.setShadowLayer(6f, 0, 0, Color.BLACK);
// paint.setStyle(Paint.Style.FILL);
// paint.setTextAlign(Paint.Align.LEFT);
}
#Override
public void draw(Canvas canvas) {
canvas.drawText(text, 0, 0, paint);
}
#Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
MyDragShadowBuilder:
private class MyDragShadowBuilder extends View.DragShadowBuilder {
// The drag shadow image, defined as a drawable thing
private Drawable shadow;
private Point mScaleFactor;
// Defines the constructor for myDragShadowBuilder
public MyDragShadowBuilder(View v, int count) {
// Stores the View parameter passed to myDragShadowBuilder.
super(v);
// Creates a draggable image that will fill the Canvas provided by the system.
// shadow = v
shadow = new TextDrawable(getActivity(),"5000");
// shadow = new ColorDrawable(Color.LTGRAY);
//ColorDrawable(Color.RED);
}
// Defines a callback that sends the drag shadow dimensions and touch point back to the
// system.
#Override
public void onProvideShadowMetrics(Point size, Point touch) {
// Defines local variables
int width, height;
// Sets the width of the shadow to half the width of the original View
width = getView().getWidth() / 2;
Log.d("TAG", "width=" + width);
// Sets the height of the shadow to half the height of the original View
height = getView().getHeight() / 2;
Log.d("TAG", "height=" + height);
// The drag shadow is a ColorDrawable. This sets its dimensions to be the same as the
// Canvas that the system will provide. As a result, the drag shadow will fill the
// Canvas.
shadow.setBounds(0, 0, width, height);
// Sets the size parameter's width and height values. These get back to the system
// through the size parameter.
size.set(width, height);
// Sets size parameter to member that will be used for scaling shadow image.
mScaleFactor = size;
// Sets the touch point's position to be in the middle of the drag shadow
touch.set(width / 2, height / 2);
}
// Defines a callback that draws the drag shadow in a Canvas that the system constructs
// from the dimensions passed in onProvideShadowMetrics().
#Override
public void onDrawShadow(Canvas canvas) {
// Draws the ColorDrawable in the Canvas passed in from the system.
shadow.draw(canvas);
// canvas.scale(mScaleFactor.x/(float)getView().getWidth(), mScaleFactor.y/(float)getView().getHeight());
// getView().draw(canvas);
}
}
Though I am little late but it might be helpful for others, your 1st approach is correct but before new MyDragShadowBuilder(view1) you need to call -
view1.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
view1.layout(0, 0,view1.getMeasuredWidth(),view1.getMeasuredHeight())
This will give a proper dimension to your custom view
I have written a custom view to display images dynamically inside a circle.
I have overwritten Viewgroup's dispatchDraw method to draw circle. After this the child ImageViews are not displaying on screen, if I do no override the method, then they are displaying on screen.
Here is my class:
public class CustomView extends RelativeLayout {
private Paint paint;
private View mView;
private Context context;
private void init(Context context) {
LinearLayout layout = new LinearLayout(context);
layout.setGravity(Gravity.CENTER);
layout.setOrientation(LinearLayout.VERTICAL);
// Set generic layout parameters
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
Button button = new Button(context);
button.setText("Button!");
layout.addView(button, params); // Modify this
ImageView imageView = new ImageView(context);
imageView.setImageResource(R.drawable.coffe_selected);
layout.addView(imageView);
this.addView(layout);
}
public CustomView(Context mContext) {
super(mContext);
context = mContext;
// create the Paint and set its color
paint = new Paint();
paint.setColor(0xFF1f5b83);
init(context);
}
#Override
protected void dispatchDraw(Canvas canvas) {
int width = this.getWidth();
int height = this.getHeight();
canvas.drawCircle(width / 2, height / 2-64, 200, paint);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Take a look at the source code of ViewGroup and what is happening in dispatchDraw.
Just one line out of it:
more |= drawChild(canvas, transientChild, drawingTime);
as you can see, the childs are drawn there.
So if you don't call the super method of dispatchDraw, it is possible that the childs are not drawn.
Simply call:
super.dispatchDraw(canvas);
I'm using RelativeLayout to absolutely position some standard views (like TextView).
What I'd like to do is to draw a custom line on this RelativeLayout's Canvas using Canvas.drawLine that is drawn behind all its other subviews.
These other subviews are added with explicitely defining RelativeLayout.LayoutParams, but I'd like to leave the decision of where to paint itself to my custom line.
I tried wrapping this line in a CustomView with overloaded View.onDraw(Canvas canvas) method and simply adding the view without specifying any LayoutParams, so:
public class CustomView extends View {
public CustomView(Context context, int x0, int y0, int x1, int y1) {
super(context);
setClickable(false);
setBackgroundColor(Color.TRANSPARENT);
}
public void onDraw(Canvas canvas) {
Log.i("myapp", "i'm not called! :(")
Paint p = new Paint();
p.setColor(Color.BLACK);
canvas.drawLine(x0, y0, x1, y1, p);
}
}
And usage:
CustomView v = new CustomView(MyActivity.this, 0, 0, 100, 100);
relativeLayout.addView(v);
... but this onDraw method is never called.
Is there a way to make this work?
Edit: works if I substitute:
relativeLayout.addView(v)
with
relativeLayout.addView(v,
new RelativeLayout.LayoutParams(SOME_WIDTH, SOME_HEIGHT));
The point is, I know neither SOME_WIDTH, nor SOME-HEIGHT at that point.
try this custom RelativeLayout:
class RL extends RelativeLayout {
private Paint mPaint;
public RL(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(5);
mPaint.setColor(0xffffffff);
}
#Override
protected void dispatchDraw(Canvas canvas) {
int cnt = getChildCount();
for (int i = 0; i < cnt; i++) {
View child = getChildAt(i);
int l = child.getLeft();
int t = child.getTop();
int r = child.getRight();
int b = child.getBottom();
if (i % 2 == 0) {
canvas.drawLine(l, t, r, b, mPaint);
} else {
canvas.drawLine(l, b, r, t, mPaint);
}
}
super.dispatchDraw(canvas);
}
}
and test it ba adding the following in onCreate() method:
RelativeLayout rl = new RL(this);
TextView tv;
List<String> list = Arrays.asList("one", " two ", "three", " four ", "fife");
int i = 0;
for (String string : list) {
int id = 1000 + i;
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
if (i != 0) {
params.addRule(RL.BELOW, id - 1);
}
tv = new TextView(this);
tv.setTextSize(48);
tv.setTextColor(0xffff0000);
tv.setText(string);
rl.addView(tv, params);
tv.setId(id);
i++;
}
setContentView(rl);
So.
I ended up creating a CustomController which has some methods to calculate position/size and using this controller when creating RelativeLayout.LayoutParams for each CustomView(context, controller).
I guess you cannot have a subview in a RelativeLayout without specifying its RelativeLayout.LayoutParams.
Easiest way is to call the super.draw(Canvas) method after you finished your background in the onDraw() method.
That will cause it to draw the children last.
public class Player extends ViewGroup {
private RectF rect = new RectF();
private Paint paint;
public Player(Context context,String pname) {
super(context);
setWillNotDraw(false);
paint=new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Style.FILL);
paint.setColor(getResources().getColor(R.color.red));
}
public void onDraw(Canvas canvas) {
canvas.drawRoundRect(rect, 10, 10, paint);
canvas.drawCircle(rect.centerX(), rect.centerY(), 10, paint);
//canvas.drawColor(Color.RED);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wspec = MeasureSpec.makeMeasureSpec(
getMeasuredWidth(), MeasureSpec.EXACTLY);
int hspec = MeasureSpec.makeMeasureSpec(
getMeasuredHeight(), MeasureSpec.EXACTLY);
for(int i=0; i<getChildCount(); i++){
View v = getChildAt(i);
v.measure(wspec, hspec);
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
rect.set(l, t,r, b);
}
}
the third command does draw a red rectangle which bounds are the rect (l,t,r,b) = (412,415,735,754) which is given by the param rect, and for some reason, the two first commands do not do any effect on the canvas!
I have made sure the rect is an actual rectangle, as i mentioned its values were (412,415,735,754) which does make a valid rectangle, and you see how i defined the paint so why the hell wouldnt it draw?
been spending 2 hours trying to figure it out, seriously...
thanks!
BTW, the class extends ViewGroup cause it eventually meant to implement a view container..
Try this for your onLayout routine:
protected void onLayout(boolean changed, int l, int t, int r, int b) {
rect.set(0, 0, r-l, b-t);
}
This way you will create a rect with the width and height of the full layout, but whose top left point (relative to the canvas) is 0, 0.