Measure a View from fragment - android

So , I have one fragment in which I have two buttons.
I want to get this button position in this fragment. So, I'm trying to do this:
Button button;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
final View w = inflater.inflate(R.layout.fragment_buttons, container, false);
button = (Button)w.findViewById(R.id.button);
final ViewTreeObserver obs = button.getViewTreeObserver();
obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw () {
Rect r = locateView(w.findViewById(R.id.button));
return true;
}
});
return w;
}
And this is how I'm getting button position
public Rect locateView(View view) {
Rect loc = new Rect();
int[] location = new int[2];
if (view == null) {
return loc;
}
view.getLocationOnScreen(location);
loc.left = location[0];
loc.top = location[1];
loc.right = loc.left + view.getWidth();
loc.bottom = loc.top + view.getHeight();
return loc;
}
However , problem is that onPreDraw is never called. What I'm doing wrong?

I've just figured out how to solve this problem.
button = (Button)w.findViewById(R.id.button);
ViewTreeObserver observer = button.getViewTreeObserver();
observer.addOnGlobalLayoutListener (new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = locateView(button);
button.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
This code is working. However, I still don't have any idea why previous version didn't work.

Related

Android - Insert textview on the touched xy position where views overlap touched close to another view

Here is the problem I am facing. On a empty relative layout, when touched textview is instantiated at the touched x y position. I got this far correct, but the problem is that when I touch on the empty space near already instantiated view, previous view and currently placed views are overlapped. I tried by the getting the child views of the layout and checking the current view and already placed view using rect data that if they intersect. How to solve this problem?
Here is the code:
public class MainActivity extends AppCompatActivity
{
private int id = 0;
private RelativeLayout root;
private RelativeLayout.LayoutParams params;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_designer);
root = (RelativeLayout) findViewById(R.id.rootlayout);
root.setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
instantiateView(v, event);
break;
}
return true;
}
});
}
private void instantiateView(View v, MotionEvent event)
{
int x = (int) event.getX();
int y = (int) event.getY();
TextView bt = new TextView(DesignerActivity.this);
bt.setText("1");
bt.setId(++id);
bt.setBackgroundColor(Color.BLACK);
bt.setTextColor(Color.WHITE);
bt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
showDialog();
}
});
params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(x, y, 0, 0);
bt.setLayoutParams(params);
//((ViewGroup) v).addView(bt);
if(root.getChildCount() <= 0)
{
((ViewGroup) v).addView(bt);
}
else
{
for (int i = 0; i < root.getChildCount(); i++)
{
if (!checkCollision(bt, root.getChildAt(i)))
{
if(bt != root.getChildAt(i))
{
((ViewGroup) v).addView(bt);
}
}
}
}
}
private void showDialog()
{
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.dialog_layout);
Button editBtn = (Button) dialog.findViewById(R.id.button1);
Button deleteBtn = (Button) dialog.findViewById(R.id.button2);
editBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View arg0)
{
dialog.dismiss();
}
});
deleteBtn.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View arg0)
{
dialog.dismiss();
}
});
dialog.show();
}
private boolean checkCollision(View v1, View v2)
{
Rect r1 = new Rect(v1.getLeft(), v1.getTop(), v1.getRight(), v1.getBottom());
Rect r2 = new Rect(v2.getLeft(), v2.getTop(), v2.getRight(), v2.getBottom());
return r1.intersect(r2);
}
}
You are using Relative Layout that's why your Textviews are overlapping.
If you don't want the overlapping and want to place it next or somewhere else to the overlapped view , it is your decision. Just check if they intersect and take appropriate decision based on your requirement.
Below line is the problem in your code.
Rect r1 = new Rect(v1.getLeft(), v1.getTop(), v1.getRight(), v1.getBottom());
You set the params' Margin does not mean that you will get desired left,top,right, bottom values.You will get these values right after the inflation of your view hierarchy.
You can use this function:
private boolean checkCollision(View v1, View v2)
{
int leftMargin = ((RelativeLayout.LayoutParams) v1.getLayoutParams()).leftMargin;
int topMargin = ((RelativeLayout.LayoutParams) v1.getLayoutParams()).topMargin;
Rect r1 = new Rect(root.getPaddingLeft() + leftMargin, root.getPaddingTop() + topMargin,
root.getPaddingLeft() + leftMargin + v2.getWidth(), root.getPaddingTop() + topMargin + v2.getHeight());
Rect r2 = new Rect(v2.getLeft(), v2.getTop(), v2.getRight(), v2.getBottom());
return r1.intersect(r2);
}
After that use
params.addRule(); according to your requirement where you want to place your overlapping view.

Retaining Fragment

I am writing an app that will draw a bubble on Canvas.
I have MainActivity with its layout as a simple LinearLayout which I use as holder for fragment.
My fragment has no xml layout as I am drawing on Canvas, so I set its layout programmatically like this:
public class BubbleFragment extends Fragment {
Bubble bubble;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//retain fragment
setRetainInstance(true);
//bubble = new Bubble(getActivity()); //THIS WILL CRASH APP, MOVE TO onCreateView instetad
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
100);
LinearLayout ll = new LinearLayout(getActivity());
ll.setOrientation(LinearLayout.VERTICAL);
// instantiate my class that does drawing on Canvas
bubble = new Bubble(getActivity());
bubble.setLayoutParams(lp);
bubble.setBackgroundColor(Color.BLUE);
ll.addView(bubble); //if you create bubble in onCreate() this will crash. Create bubble in onCreateView
return ll;
}
}
So, when I start my app, I am expecting bubble to show in the middle of the screen and move slowly towards bottom. Since I use setRetainInstance(true) above, I am expecting that when I rotate my screen, the bubble will continue where it left off before rotation. However, it is redraws at its initial location (middle of the screen).
I would like to to continue drawing itself from the position where it was before screen orientation changed, not from the beginning.
Here is my bubble code:
public class Bubble extends View {
private static final boolean BUBBLING = true; //thread is running to draw
private Paint paint;
private ShapeDrawable bubble;
// coordiantes, radius etc
private int x;
private int y;
private int dx;
private int dy;
private int r;
private int w = 400;
private int h = 400;
//handler to invalidate (force redraw on main GUI thread from this thread)
private Handler handler = new Handler();
public Bubble(Context context) {
super(context);
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
w = size.x;
h = size.y;
x = w/2;
y = h/2;
r = 60;
dx = 1;
dy = 1;
bubble = new ShapeDrawable(new OvalShape());
bubble.getPaint().setColor(Color.RED);
bubble.setBounds(0, 0, r, r);
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(10);
}
#Override
protected void onSizeChanged (int w, int h, int oldw, int oldh){
//set bubble parameters (center, size, etc)
startAnimation();
}
public void startAnimation(){
new Thread(new Runnable() {
public void run() {
while (BUBBLING) {
moveBubble();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
//update by invalidating on main UI thread
handler.post(new Runnable() {
public void run() {
invalidate();
}
});
}
}
}).start();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
// draws bubble on canvas
canvas.translate(dx, dy);
bubble.draw(canvas);
canvas.restore();
}
private void moveBubble(){
dx += 1;
dy += 1;
x = x + dx;
y = y + dy;
if (bubble.getPaint().getColor() == Color.YELLOW){
bubble.getPaint().setColor(Color.RED);
} else {
bubble.getPaint().setColor(Color.YELLOW);
}
}
}
Much appreciated,
If you really want to retain an entire view, you could do something like a lazy load:
public class BubbleFragment extends Fragment {
Bubble bubble;
LinearLayout parent;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
parent = new LinearLayout(activity);
if(bubble == null) {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
100);
bubble = new Bubble(getActivity());
bubble.setLayoutParams(lp);
bubble.setBackgroundColor(Color.BLUE);
}
parent.setOrientation(LinearLayout.VERTICAL);
parent.addView(bubble);
}
#Override
public void onDetach() {
super.onDetach();
parent.removeView(bubble);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return parent;
}
}
In your case you don't need setRetainInstance(true), you just need to save instance variables in onSaveInstanceState() and load them in onCreate(). For your example, something like:
public void onCreate(Bundle b) {
super.onCreate(b);
if(b != null) {
xPos = b.getInt("x");
yPos = b.getInt("y");
}
}
public void onSaveInstanceState(Bundle b) {
super.onSaveInstanceState(b);
b.putInt("x",xPos);
b.putInt("y",yPos);
}
By the way, the reason you can't create Bubble in onCreate() is because the Fragment is not fully associated with its Activity until onActivityCreated() is called, which comes after onCreate() is called.

I can't show a popup window below the ImageView that called

I have a ListView of items, each item have an ImageView by clicking on it displays a PopupWindow. The problem is I can not show the window below the ImageView that called. The OnClickListener of the ImageView that call the pw is implemented inside getView() method of my listadapter class.
How I can do to show the pw below the respective ImageView?
ListView
I tried to do it using this code, but it did not work. onGlobalLayout() is never called.
#Override public View getView(int position, View convertView, final ViewGroup parent) {
...
// ImageView that show the PopupWindow
holder.menuOverflow = (ImageView) row.findViewById(R.id.iv_menu_overflow_item_listview_seccion);
holder.menuOverflow.setTag(position);
holder.menuOverflow.setOnClickListener(new OnClickListener() {
#Override
public void onClick(final View v) {
ViewTreeObserver vto = (holder.menuOverflow).getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
y = (int) (holder.menuOverflow).getY();
}
});
...
// Show the PopupWindow
pw.showAtLocation(parent, Gravity.RIGHT, 0, y);
// Animation
Animation anim = AnimationUtils.loadAnimation(activity, R.anim.grow_from_top);
anim.setDuration(100);
popupView.startAnimation(anim);
}
});
return row;
}
Solved by me:
int[] _location = new int[2];
// Point (x, y) stored in array _location
// _location[0] = x, _location[1] = y.
v.getLocationOnScreen(_location);
//Initialize the Point with x, and y positions
Point p = new Point();
p.x = _location[0];
p.y = _location[1];
int OFFSET_Y = 30;
// muestra la popup window
pw.showAtLocation(activity.findViewById(R.id.sliding_panel_layout), Gravity.NO_GRAVITY, p.x, p.y + OFFSET_Y);

Android - View resource is null inside an event on a fragment in a PageViewer

i've got some trouble with PageViewer, i've searched a lot in the other questions, but it seems that no one fall in my same trouble, so i'm here to ask to the experts what i'm doing wrong.
The problem is this:
I can't figure out inflating the layout inside an event, this is the code:
// onCreateView :
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
// fragment not when container null
if (container == null) {
return null;
}
// inflate view from layout
View view = (RelativeLayout) inflater.inflate(R.layout.fragment_doorsway_lay, container, false);
// get button reference
/*
* btnWrite = (Button) view.findViewById(R.id.btWrite); // set button
* listener to trigger activity listener btnWrite.setOnClickListener(new
* View.OnClickListener() {
*
* #Override public void onClick(View v) {
* pageListener.onPage1("Page 1 to"); } }); // update text TextView tv =
* (TextView) view.findViewById(R.id.tvText1); tv.setText(ptext);
*/
View imageView = view.findViewById(R.id.clickZones);
imageView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
View view = (RelativeLayout) inflater.inflate(R.layout.fragment_doorsway_lay, container, false);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// PRESSED
try {
ImageView newView = ((ImageView) v);
Bitmap bitmap = ((BitmapDrawable) newView.getDrawable()).getBitmap();
Matrix inverse = new Matrix();
newView.getImageMatrix().invert(inverse);
float[] touchPoint = new float[] { event.getX(),
event.getY() };
inverse.mapPoints(touchPoint);
int pixel = bitmap.getPixel((int) touchPoint[0],
(int) touchPoint[1]);
int redValue = Color.red(pixel);
int greenValue = Color.green(pixel);
if (greenValue == 255) {
//pageListener.onPage1("GREEN");
view.findViewById(R.id.imgChainPressSx).setVisibility(View.VISIBLE);
//view.findViewById(R.id.imgChainPressSx).setVisibility(View.VISIBLE);
}
if (redValue == 255) {
//pageListener.onPage1("RED");
view.findViewById(R.id.imgChainPressDx).setVisibility(View.VISIBLE);
}
// new Connection().execute();
} catch (Exception e) {
Log.e("error", "" + e.getMessage());
}
return true; // if you want to handle the touch event
case MotionEvent.ACTION_UP:
// RELEASED
//view.findViewById(R.id.imgChainPressSx).setVisibility(View.VISIBLE);
//view.findViewById(R.id.imgChainPressDx).setVisibility(View.VISIBLE);
}
return true;
}
});
return view;
}
The code work, btu maybe beacause the second call to:
View view = (RelativeLayout) inflater.inflate(R.layout.fragment_doorsway_lay, container, false);
Is not referencing to the main view, the view that will be visible in the line
view.findViewById(R.id.imgChainPressDx).setVisibility(View.VISIBLE);
will not show up in the GUI.
....in realty, it's not true that i can't figure out...
With the use of:
pageListener.onPage1("GREEN");
that is a function working in the main activity, passing a string and handling it, i can get the main view only asking "findViewById" without referencing to a view, and managing it with an if statement on the string, but this is not the way i want to handle that... I understand it is not the best way to do this...
Thank you in advance for reply and best regards to all of you.
Fin3
Check this:
Is your fragment a SubClass of RegisterFragment?
Standard mistake: Is 'fragment_doorsway_lay' written right?
(Sorry that I don't comment, but i have too less reputations...)
Thank you for your reply, anyway i've found the solution... it looks like:
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
// fragment not when container null
if (container == null) {
return null;
}
// inflate view from layout
View view = (RelativeLayout) inflater.inflate(R.layout.fragment_doorsway_lay, container, false);
// get button reference
/*
* btnWrite = (Button) view.findViewById(R.id.btWrite); // set button
* listener to trigger activity listener btnWrite.setOnClickListener(new
* View.OnClickListener() {
*
* #Override public void onClick(View v) {
* pageListener.onPage1("Page 1 to"); } }); // update text TextView tv =
* (TextView) view.findViewById(R.id.tvText1); tv.setText(ptext);
*/
//View imageView = view.findViewById(R.id.clickZones);
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// PRESSED
try {
ImageView newView = ((ImageView) v.findViewById(R.id.clickZones));
Bitmap bitmap = ((BitmapDrawable) newView.getDrawable()).getBitmap();
Matrix inverse = new Matrix();
newView.getImageMatrix().invert(inverse);
float[] touchPoint = new float[] { event.getX(),
event.getY() };
inverse.mapPoints(touchPoint);
int pixel = bitmap.getPixel((int) touchPoint[0],
(int) touchPoint[1]);
int redValue = Color.red(pixel);
int greenValue = Color.green(pixel);
if (greenValue == 255) {
//pageListener.onPage1("GREEN"); //Questa funzione finisce nel Main_Activity
v.findViewById(R.id.imgChainPressSx).setVisibility(View.VISIBLE);
//view.findViewById(R.id.imgChainPressSx).setVisibility(View.VISIBLE);
}
if (redValue == 255) {
//pageListener.onPage1("RED"); //Questa funzione finisce nel Main_Activity
v.findViewById(R.id.imgChainPressDx).setVisibility(View.VISIBLE);
}
// new Connection().execute();
} catch (Exception e) {
Log.e("error", "" + e.getMessage());
}
return true; // if you want to handle the touch event
case MotionEvent.ACTION_UP:
// RELEASED
v.findViewById(R.id.imgChainPressSx).setVisibility(View.INVISIBLE);
v.findViewById(R.id.imgChainPressDx).setVisibility(View.INVISIBLE);
}
return true;
}
});
return view;
}
So, i'm not referencing to the specific view that causes the event in the onTouch overriding, but the whole view must be passed, the one given by the pageViewer, and then accessing the specific view with findViewById in the method.
Hope that this will help someone!
Good luck,
Fin3

Relative Layout get Width/Height return 0

I need to get width and height of Relative layout, however I do not know when the Relative Layout is ready to provide me these two attributes. My activity has several tabs and I call the getWidth() and getHeight() in onCreateView() after inflater.inflate(R.layout.fragment_pizza, container, false);, however it returns 0 for both of them, so they are not apparently ready. Is there some kind of event like afterCreateView() or something similar where I can call these two methods and get the actual width and height?
Code:
public class PizzaFragment extends Fragment {
private static final String TAG = "PizzaFragment";
private Controller controller;
private static final int BUTTON_WIDTH = 70;
private static final int BUTTON_HEIGHT = 20;
private static final int MARGIN_LEFT = 80;
private static final int MARGIN_BOTTOM = 30;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
controller = Controller.getInstance();
controller.loadProducts("pizza", getActivity().getApplicationContext());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_pizza, container, false);
RelativeLayout layout = (RelativeLayout) view.findViewById(R.id.rl_order_pizza);
Log.d(TAG, "Width: " + layout.getWidth());
Log.d(TAG, "Height: " + layout.getHeight());
int currentX = 10;
int currentY = 10;
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(BUTTON_WIDTH, BUTTON_HEIGHT);
for (Product product: controller.getProducts("pizza")){
Button tempButton = new Button(getActivity());
tempButton.setId(product.getId());
tempButton.setText(product.getName());
if (layout.getWidth() < currentX + MARGIN_LEFT){
currentX = 10;
currentY = MARGIN_BOTTOM;
}
else{
currentX += MARGIN_LEFT;
}
Log.d(TAG, "CurrentY: " + currentY);
Log.d(TAG, "CurrentX: " + currentX);
layoutParams.leftMargin = currentX;
layoutParams.bottomMargin = currentY;
tempButton.setLayoutParams(layoutParams);
layout.addView(tempButton);
}
return view;
}
}
My suggestion is to use a global layout listener like this in onCreateView:
YOUR_LAYOUT_INSTANCE = view.findViewById(R.id.YOUR_LAYOUT_ID);
...
YOUR_LAYOUT_INSTANCE.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
// gets called after layout has been done but before display.
if (Build.VERSION.SDK_INT < 16) {
YOUR_LAYOUT_INSTANCE.getViewTreeObserver().removeGlobalOnLayoutListener(this);
} else {
YOUR_LAYOUT_INSTANCE.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
// get width and height
}
});
addOnGlobalLayoutListener is deprecated use removeOnGlobalLayoutListener .For better handling extending the solution provided above by #fasteque
YOUR_LAYOUT_INSTANCE.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
// gets called after layout has been done but before display.
if (Build.VERSION.SDK_INT < 16) {
YOUR_LAYOUT_INSTANCE.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
else {
YOUR_LAYOUT_INSTANCE.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
// get width and height
}
});

Categories

Resources