I am writing a custom view where I draw myself to the canvas in the onDraw(Canvas canvas) method.
My idea is to also animate the view as soon as it becomes visible by using an ObjectAnimator and a setter for a particular variable in my view. Class looks as follows:
public DrawView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, -1);
}
public DrawView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DrawView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr);
}
private void init(final Context context, AttributeSet attrs, int defStyleAttr) {
anim = ObjectAnimator.ofInt(this, PROGRESS, 0, 100);
anim.setDuration(animDuration);
if (getVisibility() == VISIBLE) {
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
getViewTreeObserver().removeOnPreDrawListener(this);
animate();
return true;
}
});
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw something here using progress
}
protected void setProgress(final int pathProgressParam) {
// Re-draw
invalidate();
}
}
When triggering the animation, the "setProgress" method is called properly, and the View gets invalidated to force a re-draw with the new value for my progress field.
The problem comes when sometimes, let's say 30% of the times, the onDraw method is not called even though the View gets marked as "dirty", and only the very last frame is drawn, which kinda defeats the purpose of the animation.
I do not know whether there is any better step in the drawing cycle to trigger the animation, of if I should trigger it from somewhere else as well, but it seems as invalidate() is not enough. I have also looked at how Android guys perform property animations, but unfortunately the methods they used are hidden.
Btw, I have used the same idea as Romain Guy did here: https://github.com/romainguy/road-trip
Related
I see a class that use this class for show images in square.
i can't understand what is this class? have any feature for show images in square?
this code used in layout xml file instead of RelativeLayout
class SquareLayout extends RelativeLayout {
public SquareLayout(Context context) {
super(context);
}
public SquareLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SquareLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Set a square layout.
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
The important line is super.onMeasure(widthMeasureSpec, widthMeasureSpec);. As you can see, the height is replace with the width. So you have width x width instead of width x height. That is a square. onMeasure is used to tell the system the requested Area of the view. Hope it helps
On Android phone, I get the information of co-ordinates that, user touch on his/her phone screen or device. How do i then, use/pass these X-Y coordinates to run a specific function of mine, and use it in my code
reference from the post you can get the co-ordinates if the user touched the screen on your app. After you implement that on touch listener then in the if statement MotionEvent.ACTION_UP put this there
float myX = event.getX();
float myY = event.getY();
// now you can then pass myX,myY in your method or function as parameters
if you happen to be targeting Api 14+ then you can use onhover with the same approach as to the first one from that post, instead of using setOnTouchListener usegetWindow().getDecorView().setOnHoverListener() that's about it.
A side note is using the firs one is better because onHover can not be trusted to work,actually it doesn't work to me, thats just btw
hope i answered your question.
You can create custom view by extending android view and overriding onTouchEvent method. For example,
public class NotTouchableWebView extends WebView {
private Context context;
public NotTouchableWebView(Context context) {
super(context);
this.context=context;
}
public NotTouchableWebView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context=context;
}
public NotTouchableWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context=context;
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public NotTouchableWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
this.context=context;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (some condition){
//your specific function
return false;
}
return super.onTouchEvent(event);
}
}
I am trying to create a 2D avatar customizer without using a Game Engine inside my Android app.
I will basically have a PNG image as a base to start with. Then I want to layer other images on top of the base to customize the character.
What would be the best option to create this? I have already created a custom ImageView and overrided the onDraw():
public class AvatarView extends ImageView {
public AvatarView(Context context) {
super(context);
}
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AvatarView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.battle_run_char), 0, 70, null);
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.red_cartoon_hat), 70, 0, null);
}
}
This seems very specific by using coordinates. Would there be a better way to achieve this without having to use coordinates?
EDIT:
Here is what I have so far. The guy is a separate image from the red hat.
If you are planning to add on that ImageView dynamically then there is not way to place those images without assigning pixel axis on it
one problem in your custom class is that dont ever decodeResource within your onDraw method that it will be called multiple times and it will cause a big lag problem, instead create an init method within your AvatarView and decode it, and call that init method in all of your constructor.
sample:
public class AvatarView extends ImageView {
private Bitmap body;
private Bitmap hat;
public AvatarView(Context context) {
super(context);
init();
}
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AvatarView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init()
{
body = getResources(), R.drawable.battle_run_char);
hat = getResources(), R.drawable.red_cartoon_hat);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(BitmapFactory.decodeResource(body , 0, 70, null);
canvas.drawBitmap(BitmapFactory.decodeResource(hat , 70, 0, null);
}
}
I have a dialog box that contains a Scrollview, which contains a layout with two TimePickers.
The timepickers are the newer style ones, what's in ICS.
The problem is that they seem to fight for focus when you change the time by dragging the wheel, or flicking it. It will change the time just a little, and then the layout will scroll instead.
Any ideas?
Thanks in advance.
I had the same problem when using the Holo theme, and here is where I found the solution: https://groups.google.com/forum/?fromgroups#!topic/android-developers/FkSfJI6dH8w
You must implement your custom DatePicker or TimePicker and override the following method:
#Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN)
{
ViewParent p = getParent();
if (p != null)
p.requestDisallowInterceptTouchEvent(true);
}
return false;
}
Because the link from Klemens Zleptnig is broken, here is a complete example. This fix helps with the scroll of a TabLayout too btw. I excluded the area around the big numbers in the top of the TimePicker because they don't need the scroll event anyway.
xml:
<com.name.app.MyTimePicker
android:id="#+id/timePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
.../>
java:
public class MyTimePicker extends TimePicker {
public MyTimePicker(Context context) {
super(context);
}
//This is the important constructor
public MyTimePicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTimePicker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyTimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
//Excluding the top of the view
if(ev.getY() < getHeight()/3.3F)
return false;
ViewParent p = getParent();
if (p != null)
p.requestDisallowInterceptTouchEvent(true);
}
return false;
}
}
how can I find out when onDraw of a View has finished?
Thanks!
I don't know what you're ultimately trying to achieve, but if you need to run some logic once drawing has been completed in an Activity, then you can call View.post(Runnable) and put your logic inside there. onDraw would have likely taken place once the code in your Runnable has been reached since it put that Runnable on the message queue.
Place a boolean member in your View class and set it to true when onDraw is called (If you simply want to be able to test that a View has drawn).
Or if you want some sort of post-draw "event" call to execute a new thread- just put it at the end of onDraw.
Another suggestion how it could be done:
public class FVRTraceAbleListView extends ListView {
ListViewListener listener;
public interface ListViewListener {
void onPostDraw();
}
public FVRTraceAbleListView(Context context) {
super(context);
}
public FVRTraceAbleListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FVRTraceAbleListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public FVRTraceAbleListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setListener(ListViewListener listener) {
this.listener = listener;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (listener != null) {
listener.onPostDraw();
}
}
}