I need view width and height to use in some calculations in activity.
In a SO Q&A i saw
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth, parentHeight);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
It works but my questions:
1)how can i pass it to activity?
2) Or should i put my CustomView as a inner class into the Activity file like this
public class MainActivity extends ActionBarActivity {
int parentHeight=0;
int parentWidth=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle aBundle=new Bundle();
MyView myView = new MyView(this);
Binary c=calculateCoordinates();
aBundle.putSerializable("coordinate", c);
myView.setData(aBundle);
myView.setBackgroundColor(Color.WHITE);
setContentView(myView);
}
public Binary calculateCoordinates(){
Random generator = new Random();
Binary c=new Binary();
c.setX(generator.nextInt(parentWidth-1));
c.setY(generator.nextInt(parentHeight-1));
return c;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//int action = event.getAction();
switch (event.getAction()){
case MotionEvent.ACTION_UP:
Intent intent = getIntent();
finish();
startActivity(intent);
break;
}
return true;
}
public class MyView extends View {
Bundle bundle = new Bundle();
Paint paint = new Paint();
public MyView(Context context) {
super(context);
}
#Override
public void onDraw(Canvas canvas) {
paint.setColor(Color.BLACK);
paint.setStrokeWidth(3);
Binary c = (Binary)bundle.getSerializable("coordinate");
canvas.drawRect(c.getX(), c.getY(), c.getX()+1, c.getY()+1, paint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
parentWidth = MeasureSpec.getSize(widthMeasureSpec);
parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth, parentHeight);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void setData(Bundle bundle){
this.bundle=bundle;
}
}
}
But if so i can not know when measurement have been got exactly, and if it has been got before i call the coordinate calculation.
Activity Class
add this to your OnCreate code.
final ViewTreeObserver obs = myView.getViewTreeObserver();
obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw () {
int height = myView.getHeight();
int width = myView.getWidth();
}
when the View will be drawn this function will be called with the correct width and height.
The OnMeasure function within your view will get called once the View has been placed on your actual page.
So let's say you want to get the Width and the Height when you click a button or something and want to pass those values over to another activity.
Put your view in your XML file:
<your.namespace.app.MyView
xmlns:app="http://schemas.android.com/apk/res/novoda.tastecard"
android:id="#+id/myView"
android:layout_width="match_parent"
...../>
Then reference your View in your activity:
mMyView = findViewById(R.id.myView);
Then you can then just do:
int width = myView.getWidth();
int height = myView.getHeight();
That should return you the width and the height of the view.
Then do the following to call the new Activity with your values:
Intent intent = new Intent(this, NewActivity.class);
intent.putExtra("width", width);
intent.putExtra("height", height);
startActivity(intent);
Related
i am drawing a sliderbar when a page swipe occurs in viewpager. So i draw it in ViewPager onPageChange event. Problem is; onDraw method is (sometimes) not called. THE SAME CODE WORKS IN OTHER PROJECT BUT NOT IN THIS ONE which makes it weirder.
My SliderBar View Class;
public class SliderBar extends View {
Paint p = new Paint();
int startX;
int width;
Rect r = new Rect();
public SliderBar(Context c,int x,int width){
super(c);
this.startX = x;
this.width = width;
this.setWillNotDraw(false);// these didnt fix
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
p.setColor(Color.parseColor("#bbbbbbbb"));
p.setAntiAlias(true);
p.setStyle(Paint.Style.FILL);
r.set(startX,0,startX+width,500);
canvas.drawRect(r,p);
}
}
My Call Location
cvp.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int width = cvp.getWidth()/btnCount;
int currentPos = cvp.getCurrentItem();
int startX;
if(currentPos> position)
startX = (currentPos-1) * width - (cvp.getWidth()-positionOffsetPixels)/btnCount;
else
startX = (currentPos-1) * width + positionOffsetPixels/btnCount;
SliderBar dsb = new SliderBar(getBaseContext(),startX,width);
ll.removeAllViews();
ll.addView(dsb,lp);
ll.invalidate(); // these didnt fix
mainLayout.invalidate();// these didnt fix
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
});
My LinearLayout
android:layout_width="match_parent" android:orientation="vertical"
android:background="#77000000"
android:layout_height="0dp"
android:layout_weight="1" android:id="#+id/ll"
Try add flag FLAG_ACTIVITY_NO_ANIMATION in your intent.
Intent intent = new Intent(SourceActivity.this, TargetActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(intent);
Issue related commit on google source
View#invalidate() may has no effect while Activity doing window animations. So FLAG_ACTIVITY_NO_ANIMATION may resolve your issue.
In my case onDraw() method not call during animation frame update.
I want to change the visibility of a particular layout when keyboard appears .How can i create a listener for that?
Make a class for Soft Keyboard Detection
public class RelativeLayoutThatDetectsSoftKeyboard extends RelativeLayout {
public RelativeLayoutThatDetectsSoftKeyboard(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public interface Listener
{
public void onSoftKeyboardShown(boolean isShowing);
}
private Listener listener;
public void setListener(Listener listener)
{
this.listener = listener;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int height = MeasureSpec.getSize(heightMeasureSpec);
Activity activity = (Activity)getContext();
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
int diff = (screenHeight - statusBarHeight) - height;
if (listener != null)
{
listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Extend your activity with RelativeLayoutThatDetectsSoftKeyboard class and write below code in your activity
// To scroll layout when keyboard is visible
RelativeLayoutThatDetectsSoftKeyboard mChkKeyBoradVisibility;
mChkKeyBoradVisibility = (RelativeLayoutThatDetectsSoftKeyboard) mTabLogin_Layout.findViewById(your layout id);
mChkKeyBoradVisibility.setListener(this);
I want to show 3x3 sized gridview. I want to set the height and width based on device size. I am taking reference from this link.
MainActivity-
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
activity_main-
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"
/>
Edit-
Like first getting screen height and width then each item height and width 1/3 of the value of screen height and width I am getting.
Do not use screen size, in a multi-windows context this method is invalid.
If your grid is a 3x3 items size fixed, so use custom layout ViewGroup like this: (and set RelativeLayout items content)
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle state) {
setContentView(new ViewGroup(this) {
private RelativeLayout[] items = new RelativeLayout[9];
private int width, height, itemWidth, itemHeight;
{
Random r = new Random();
for (int i = 0; i < 9; i++) {
items[i] = new RelativeLayout(getContext());
float[] hsv = new float[] {360 * r.nextFloat(), .50f, .75f};
items[i].setBackgroundColor(Color.HSVToColor(hsv));
addView(items[i]);
// UPDATE ////////////////////////////////////
ImageView image = new ImageView(getContext());
switch (i) {
case 0: // top left
case 1: // top center
case 2: // top right
case 3: // center left
case 4: // center center
case 5: // center right
case 6: // bottom left
case 7: // bottom center
case 8: // bottom right
image.setImageResource(R.drawable.ic_launcher);
break;
}
image.setScaleType(ScaleType.FIT_XY);
image.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
));
items[i].addView(image);
//////////////////////////////////////////////
}
}
#Override
protected void onMeasure(int wMS, int hMS) {
width = MeasureSpec.getSize(wMS);
height = MeasureSpec.getSize(hMS);
itemWidth = width / 3;
itemHeight = height / 3;
wMS = MeasureSpec.makeMeasureSpec(itemWidth, MeasureSpec.EXACTLY);
hMS = MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY);
measureChildren(wMS, hMS);
setMeasuredDimension(width, height);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < 9; i++) {
l = itemWidth * (i % 3);
t = itemHeight * (i / 3);
r = l + itemWidth;
b = t + itemHeight;
items[i].layout(l, t, r, b);
}
}
});
super.onCreate(state);
}
}
EDIT : see my update in code, you have simply to add your images to the items containers. With this method, no XML layout file needed because you manage content and size yourself.
Result :
EDIT : the minimalist way:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle state) {
setContentView(new ViewGroup(this) {
private static final int SIZE_X = 3;
private static final int SIZE_Y = 3;
private ImageView[] items = new ImageView[SIZE_X * SIZE_Y];
private int itemWidth, itemHeight;
{
setBackgroundColor(Color.DKGRAY);
for (int i = 0; i < items.length; i++) {
items[i] = new ImageView(getContext());
items[i].setScaleType(ScaleType.CENTER);
items[i].setImageResource(R.drawable.ic_launcher);
addView(items[i]);
}
}
#Override
protected void onMeasure(int wMS, int hMS) {
int width = MeasureSpec.getSize(wMS);
int height = MeasureSpec.getSize(hMS);
itemWidth = width / SIZE_X;
itemHeight = height / SIZE_Y;
wMS = MeasureSpec.makeMeasureSpec(itemWidth, MeasureSpec.EXACTLY);
hMS = MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY);
measureChildren(wMS, hMS);
setMeasuredDimension(width, height);
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < items.length; i++) {
l = itemWidth * (i % SIZE_X);
t = itemHeight * (i / SIZE_X);
r = l + itemWidth;
b = t + itemHeight;
items[i].layout(l, t, r, b);
}
}
});
super.onCreate(state);
}
}
Result :
# Kanwaljit Singh:
In MainActivity items creation loop:
final int id = i;
items[i].setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getContext(), NextActivity.class).putExtra("id", id));
}
});
In NextActivity:
int id = getIntent().getIntExtra("id", -1);
You can get the screen dimensions like:
final DisplayMetrics displayMetrics=getResources().getDisplayMetrics();
final float screenWidthInDp=displayMetrics.widthPixels;
Log.WTF("ScreenWidth", "width: "+screenWidthInDp+", menuWidth: "+screenWidthInDp/3);
And for the gridview, I would suggest you to take a look at this awesome library called Staggered Grid View. And their sample here.
I use the following approach when I have to resize my Activitys, I assume the same is valid for your case:
// I'm storing the size of the window in the display var, so I can then play around
final Display display = getWindowManager().getDefaultDisplay();
final Point size = new Point();
display.getSize(size);
// In your case, you'll probably need something like this:
GridView gv = (GridView) findViewById(R.id.gridview);
gv.setWidth((int) size.x * 0.75); // Whis would resize the grid width to the 75%
gv.setHeight((int) size.y * 0.5); // Same with the height, but to the 50%
In the view hierarchy:
Window > ViewGroup (root) > ViewGroup[...] > View (child)
I need to know root dimension in a profound child onMeasure event.
Exemple:
#Override
protected void onMeasure(int wMS, int hMS) {
int desiredW = Math.round(rootW * factorW);
int desiredH = Math.round(rootH * factorH);
/* ... compute final dimensions ... */
setMeasuredDimension(finalW, finalH);
}
Note: At this moment, getRootView and getWindow dimentions equals to 0 because children have to setMeasuredDimention before their parents
Considering a lot of children needing this dimension, to do it:
I created an interface:
public interface OnRootSizeChanged {
public void onRootSizeChanged(int w, int h);
}
I implemented my child which now implements OnRootSizeChanged inteface:
private int rootW;
private int rootH;
#Override
public void onRootSizeChanged(int w, int h) {
rootW = w;
rootH = h;
}
I implemented root view:
#Override
protected void onMeasure(int wMS, int hMS) {
int w = MeasureSpec.getSize(wMS);
int h = MeasureSpec.getSize(hMS);
dispatchOnRootSizeChange(this, w, h);
super.onMeasure(wMS, hMS);
}
private void dispatchOnRootSizeChange(ViewGroup v, int w, int h) {
for (int i = 0, n = v.getChildCount(); i < n; i++) {
View child = v.getChildAt(i);
if (child instanceof OnRootSizeChanged)
((OnRootSizeChanged) child).onRootSizeChanged(w, h);
if (child instanceof ViewGroup)
dispatchOnRootSizeChange((ViewGroup) child, w, h);
}
}
My question is:
Have I simpler way to do this without recursivity or with better practice ?
Update: This method is invalid in case of ViewPager element in ViewGroup[...] breabcrumb. When ViewPager instantiate children pages, they have not yet received OnRootSizeChanged event so:
Children have to ask the root dimension, no the root to tell his dimension to their children
So I searched how to target root from a profound child to ask him:
getRootView() not seems targeting the view attached with setContentView()
getWindow().getDecorView() either
One possible way is:
On child:
#Override
protected void onMeasure(int wMS, int hMS) {
ViewParent parent = getParent();
while (parent instanceof RootViewClass == false)
parent = parent.getParent();
RootViewClass root = (RootViewClass) parent;
int desiredW = Math.round(root.w * factorW);
int desiredH = Math.round(root.h * factorH);
/* ... compute final dimensions ... */
setMeasuredDimension(finalW, finalH);
}
On root instance of RootViewClass:
public int w, h;
#Override
protected void onMeasure(int wMS, int hMS) {
w = MeasureSpec.getSize(wMS);
h = MeasureSpec.getSize(hMS);
super.onMeasure(wMS, hMS);
}
But with lot of children, I don't think this is a good practice. If I could find root view without use of loop.
You can forward the parent's size by storing those values in the onMeasure() method as you receive them and then letting the children access the values in their onMeasure() method through the Context reference:
// simple interface
public interface ParentRef {
void YourViewGroup getRoot();
}
// the Activity implements the interface above
public class YourActivity extends Activity implements ParentRef {
private YourViewGroup mRoot;
//in onCreate initialize the mRoot reference
#Override
public YourViewGroup getRoot() {
return mRoot;
}
//... rest of the Activity
}
// the custom ViewGroup will store the dimensions:
//fields in the root view
private int mCurWidth;
private int mCurHeight;
#Override
protected void onMeasure(int wMS, int hMS) {
int w = MeasureSpec.getSize(wMS);
int h = MeasureSpec.getSize(hMS);
mCurWidth = w;
mCurHeight = h;
// now as the children are measured they can see the values above
super.onMeasure(wMS, hMS);
}
public int getStoredWidth() {
return mCurWidth;
}
public int getStoredHeight() {
return mCurHeight;
}
// in the children's onMeasure simply do:
#Override
protected void onMeasure(int wMS, int hMS) {
final YourViewGroup root = ((ParentRef) getContext()).getRoot();
//width root.getStoredWidth()
// height root.getStoredHeight()
/* ... compute final dimensions ... */
setMeasuredDimension(finalW, finalH);
}
You can use a ViewTreeObserver (http://developer.android.com/reference/android/view/ViewTreeObserver.html)
//...get your view, than attach a viewTreeObserver on it!
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//misure the view here, like view.getHeight()
}
});
I want animation like expanding of the photos when I click folder of photos at gallery, like in this video about Android gallery.
i have two views in same custom viewgroup
view1 is in 0,0
view2 is in 100,100
since click "start" view1 will move to 100,0 and view2 will move to 0,100
My solution so far:
I use timer for refresh layout with new position by requestlayout.
the views' position will refresh by onLayout:
It works but it's not a native function and it is very slow with 100 views moving at same time.
Full code:
private class MyViewGroup extends ViewGroup{
View view1,view2;
Button btn;
public MyViewGroup(Context context) {
super(context);
view1=new View(context);
view1.setBackgroundColor(Color.RED);
this.addView(view1);
view2=new View(context);
view2.setBackgroundColor(Color.BLUE);
this.addView(view2);
btn=new Button(context);
btn.setText("start");
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
xLayout=0;
handler.sendEmptyMessage(0);
}
});
this.addView(btn);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h= MeasureSpec.getSize(heightMeasureSpec);
int widthSpec = MeasureSpec.makeMeasureSpec(50, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(50, MeasureSpec.EXACTLY);
view1.measure(widthSpec,heightSpec);
view2.measure(widthSpec,heightSpec);
btn.measure(widthSpec,heightSpec);
this.setMeasuredDimension(w, h);
}
private int xLayout=0;
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
view1.layout(xLayout,0,xLayout+50,50);
view2.layout(100-xLayout,100,150-xLayout,150);
btn.layout(0,200,50,250);
}
private void startAnimation(){
Timer timer = new Timer() ;
timer.schedule(new TimerTask(){
#Override
public void run() {
if(xLayout<100){
handler.sendEmptyMessage(0);
}
this.cancel();
}
},5);
}
private Handler handler=new Handler(){
#Override
public void handleMessage(Message msg) {
xLayout=xLayout+20;
view1.requestLayout();
startAnimation();
}
} ;
}
http://android-developers.blogspot.fr/2011/05/introducing-viewpropertyanimator.html
myView.animate().x(500).y(500);
we can use "LayoutTransition" as
final LayoutTransition transitioner = new LayoutTransition();
myViewGroup.setLayoutTransition(transitioner);
and when we click "start" addview
full code
private class MyViewGroup extends ViewGroup{
View view1,view2;
Button btn;
public MyViewGroup(final Context context) {
super(context);
view1=new View(context);
view1.setBackgroundColor(Color.RED);
this.addView(view1);
view2=new View(context);
view2.setBackgroundColor(Color.BLUE);
this.addView(view2);
btn=new Button(context);
btn.setText("start");
btn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
xLayout+=100;
MyViewGroup.this.addView(new View(context));
}
});
this.addView(btn);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int w = MeasureSpec.getSize(widthMeasureSpec);
int h= MeasureSpec.getSize(heightMeasureSpec);
int widthSpec = MeasureSpec.makeMeasureSpec(50, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(50, MeasureSpec.EXACTLY);
view1.measure(widthSpec,heightSpec);
view2.measure(widthSpec,heightSpec);
btn.measure(widthSpec,heightSpec);
this.setMeasuredDimension(w, h);
}
private int xLayout=0;
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
view1.layout(xLayout,0,xLayout+50,50);
view2.layout(100-xLayout,100,150-xLayout,150);
btn.layout(0,200,50,250);
}
}