I'm implementing a time picker which looks like this:
That yellow block is MyCustomView
. When moving MyCustomView
, I should calculate new date and set tvDate
.
partial layout file:
<LinearLayout
android:orientation="vertical">
<TextView android:id="@+id/tv_date">
<RelativeLayout
android:id="@+id/rl">
<MyCustomView
android:layout_centerInParent="true"/>
<OtherViews/>
</RelativeLayout>
</LinearLayout>
code:
class MyCustomView extends View{
// move listener
public interface IMoveCallback{
void update(int index);
}
private IMoveCallback listener = null;
// set listener
public void setMoveCallback(IMoveCallback callback){
this.listener = callback;
}
@Override
protected void onDraw(Canvas c){
super.onDraw(c);
// draw yellow block and four arrows here.
}
@Override
public boolean onTouch(View v, MotionEvent event) {
processDrag(v, event);
invalidate();
return false;
}
private void processDrag(View v, MotionEvent event){
// calculate new position(left, top, right, bottom)
v.layout(newLeft, newTop, newRight, newBottom);
if(listener != null){
// calculate index by new position
listener.update(index);
}
}
}
class MainActivity extends Activity implements MyCustomView.IMoveCallback{
MyCustomView view; // view.setMoveCallback(MainActivity.this)
@Override
public void update(int index){
tvDate.setText(String.valueOf(System.currentTimeMillis()))//update tvDate
}
}
If tvDate.setText()
is removed, MyCustomView
follows the finger, like this:
If I update tvDate
, MyCustomView
moves back to the center of rl
:
I don't think it an activity-lifecycle issue. Someone mentioned ((MarginLayoutParams)rl.getLayoutParams()).topMargin
but did not explain why.
Anybody can help me?
Your solution is to invalidate
your CustomView
after you TextView.setText()
setText()
before you layout or pass in values for your left, rigth, ...
private void processDrag(View v, MotionEvent event){
if(listener != null){
// calculate index by new position
listener.update(index);
}
// calculate new position(left, top, right, bottom)
v.layout(newLeft, newTop, newRight, newBottom);
}
//i think you should take out invalidate() in your onTouch()
Why? TextView.setText()
triggers invalidate
& requestLayout()
at them same time to force a quick layout, but Invalidate()
just tells its parent that it is dirty, so it needs to do a top down layout pass.
(im confused so i won't continue to back this up, hence i jump). Your TextView
is inside a parent layout who's grand child is your custom view, and invalidate()
, re-lays all of them hence sends your custom view back to its position, however if you exclude that, and call an explicit invalidate()
in your Custom view it tells it parent that its dirty and the same process happens, but this time only with the RelativeLayout
user: Zaid Qureshi has already made this points known to you.
Also i do not know if you know that the layoutParams being given to your customView is from the parent who you do not control much but the os, and the injected Params are what is uses to layout and give padding etc etc to your View, and since you do not lay them out but just passes positions.
Hope i make sense to you & it helps
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With