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();
}
}
}
Related
I have a xml layout with an element i use multiple times... so i decided to use <include> in the xml to avoid excess code.
My problem is that i want to make a class that is connected to this included xml component and reference to it, rather than writing the same code multiple times.
I tried to read up on a custom view component and created a class ParcelPopbarView:
public ParcelTopBarView extends View {
/...
public ParcelTopBarView(Context context, ParcelListItem parcelListItem) {
super(context);
this.parcelListItem = parcelListItem;
this.context = context;
titleTextView = findViewById(R.id.title_textview);
subtitleTextView = findViewById(R.id.subtitle_textview);
deliveryInfoTextView = findViewById(R.id.delivery_info_textview);
thumbnailLogo = findViewById(R.id.thumbnail_logo);
}
public void setTopbar(){
titleTextView.setText("hello!");
}
But i felt like custom views mostly is about drawing on a canvas, i am not doing that... so i dont know if using view is right either.
Either way, the titleTextView is null cause it cannot find the refrence to the xml file, and i have no idea how to reference to it hahah.
Does anyone have a smart solution to how i can do this the right way?
You need to inflate the layout for all the constructors of your custom view to be able to access them later.
public class ParcelTopBarView extends View {
public ParcelTopBarView(Context context) {
super(context);
init();
}
public ParcelTopBarView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ParcelTopBarView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public ParcelTopBarView(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
View view = inflate(getContext(), R.layout.parcel_top_bar, this);
titleTextView = findViewById(R.id.title_textview);
subtitleTextView = findViewById(R.id.subtitle_textview);
deliveryInfoTextView = findViewById(R.id.delivery_info_textview);
thumbnailLogo = findViewById(R.id.thumbnail_logo);
}
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
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 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
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);
}