Finding the center of the view before inflating

One more challenge was awaiting, I needed the center of the Relative Layout(root) to inflate the image dynamically at that location when admin wants to add new table in the floomap.

But the problem was – the height of Relative Layout Floormap(root) was not hardcoded. It adjusted according to screen size.

content_floormap.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:layout_weight="1"
    tools:context="com.reservation.restaurant.Fragments.FragmentFloormap">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.9"
        android:id="@+id/root">

    </RelativeLayout>
    <Button
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.1"
        android:background="#fff"
        android:textColor="@color/colorPrimary"
        android:layout_alignParentBottom="true"
        android:text="Edit"
        android:id="@+id/tableState"/>

</LinearLayout>

 

I solved the problem using ViewTreeObserver OnGlobalLayoutListener.  Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.

Following is the code-


ViewTreeObserver vto = root.getViewTreeObserver();
 vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
 @Override
 public void onGlobalLayout() {
 rootWidth = root.getWidth();
 rootHeight = root.getHeight();
 if (Build.VERSION.SDK_INT &amp;amp;lt; 16) {
 root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
 } else {
 root.getViewTreeObserver().removeOnGlobalLayoutListener(this);
 }
 centerX = rootWidth/2;
 centerY = rootHeight/2;
 viewTablesOnScreen();
 }
 });

 

There’s lot of learning while we are stuck in a problem. Waiting for new challenge…. 😀.

 

Adjusting the Location of Image according to Density

I found an issue while testing the app in other android phones. The location of image fetched from Database were not accurately shown on different phones. After searching, I found that this problem arises because of different densities of screens.

This stackoverflow answer served the purpose. Now, I am calculating the Screen Adjust parameter depending on the screen density.


DisplayMetrics displaymetrics = new DisplayMetrics();&amp;amp;nbsp; DisplayMetrics displaymetrics = new DisplayMetrics();&amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp; 

getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);&amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp; 

densityDpi = displaymetrics.densityDpi;&amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp; &amp;amp;nbsp; 

screenAdjust = densityDpi/160f;

 

In the OnDragTouchListener, I made a little modification while storing the coordinates of image in Hashmap.


FragmentFloormap.map.get(v).setX_coord(bounds[0]/FragmentFloormap.screenAdjust);

FragmentFloormap.map.get(v).setY_coord(bounds[1] /FragmentFloormap.screenAdjust);

 

Its interesting to cope up with challenges. 😎

 

Finally Floormap is Ready..

It was challenging day. I have tested the last solution for movement of view only for one image. But now, I had to load the images dynamically and move them separately. After lot of struggling with ideas, I found the solution at last.

Here’s the screenshot-

floormap_editfloormap_save

Solution:

There is a Hashmap, which stores reference of Linear Layout(consisting of image of table and unique name of table) as key and reference of TableOnScreen Bean as value.

public static HashMap<LinearLayout, TableOnScreen> map;

 

TableOnScreen.java:


public class TableOnScreen implements Serializable{

int tId;
 float x_coord;
 float y_coord;
 String tableName;

//constructor

//getters and setters

}

In the OnDragTouchListener class-


switch (event.getAction()) {
 case MotionEvent.ACTION_CANCEL:
 case MotionEvent.ACTION_UP:

 FragmentFloormap.map.get(v).setX_coord(bounds[0]);
 FragmentFloormap.map.get(v).setY_coord(bounds[1]);

 onDragFinish();
 break;
 case MotionEvent.ACTION_MOVE:
 mView.animate().x(bounds[0]).y(bounds[1]).setDuration(0).start();
 break;
}

Solving the issues of Motion Event Listener

Firstly, I struggled a lot with Drag and Drop API, but no problem got solved. I searched a lot if could solve the problems of any of the two APIs I worked on.

At last, I found the solution for Motion Event Listener in stackoverflow.

Below is the code which helped us with the movement of view across the screen-


public class OnDragTouchListener implements View.OnTouchListener{

public interface OnDragActionListener {

void onDragStart(View view);
void onDragEnd(View view);
}

private OnDragActionListener mOnDragActionListener;

public OnDragTouchListener(View view) {
this(view, (View) view.getParent(), null);
}

public OnDragTouchListener(View view, View parent) {
this(view, parent, null);
}

public OnDragTouchListener(View view, OnDragActionListener onDragActionListener) {
this(view, (View) view.getParent(), onDragActionListener);
}

public OnDragTouchListener(View view, View parent, OnDragActionListener onDragActionListener) {

initListener(view, parent);
setOnDragActionListener(onDragActionListener);
}

public void setOnDragActionListener(OnDragActionListener onDragActionListener) {
mOnDragActionListener = onDragActionListener;
}

public void initListener(View view, View parent) {
mView = view;
mParent = parent;
isDragging = false;
isInitialized = false;
}

public void updateBounds() {
updateViewBounds();
updateParentBounds();
isInitialized = true;
}

public void updateViewBounds() {
width = mView.getWidth();
xWhenAttached = mView.getX();
dX = 0;

height = mView.getHeight();
yWhenAttached = mView.getY();
dY = 0;
}

public void updateParentBounds() {
maxLeft = 0;
maxRight = maxLeft + mParent.getWidth();

maxTop = 0;
maxBottom = maxTop + mParent.getHeight();
}

@Override
public boolean onTouch(View v, MotionEvent event) {
if (isDragging) {
float[] bounds = new float[4];
// LEFT
bounds[0] = event.getRawX() + dX;
if (bounds[0] < maxLeft) {
bounds[0] = maxLeft;
}
// RIGHT
bounds[2] = bounds[0] + width;
if (bounds[2] > maxRight) {
bounds[2] = maxRight;
bounds[0] = bounds[2] - width;
}
// TOP
bounds[1] = event.getRawY() + dY;
if (bounds[1] < maxTop) {
bounds[1] = maxTop;
}
// BOTTOM
bounds[3] = bounds[1] + height;
if (bounds[3] > maxBottom) {
bounds[3] = maxBottom;
bounds[1] = bounds[3] - height;
}

switch (event.getAction()) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
onDragFinish();
break;
case MotionEvent.ACTION_MOVE:
mView.animate().x(bounds[0]).y(bounds[1]).setDuration(0).start();
break;
}
return true;
} else {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isDragging = true;
if (!isInitialized) {
updateBounds();
}
dX = v.getX() - event.getRawX();
dY = v.getY() - event.getRawY();
if (mOnDragActionListener != null) {
mOnDragActionListener.onDragStart(mView);
}
return true;
}
}

return false;
}

private void onDragFinish() {
if (mOnDragActionListener != null) {
mOnDragActionListener.onDragEnd(mView);
}

dX = 0;
dY = 0;
isDragging = false;
}

}

I have tested this with one static Image View. Tomorrow, I will inflate the Images dynamically depending on the count of tables in a particular category.

 

 

Drag and Drop

I tried solving the problems with MotionEvent Listener, but I got no success. I moved on to study Drag and Drop. Some of the features of this API-

  • It allow to move data within the Activity layout using graphical gestures.
  • Supports operations besides data movement.
  • Requires API 11.

Some sources I mentioned below, were of great help in having a clear understanding of this API-

I tried the code below-


class FloormapActivity implements OnDragListener {
Drawable enterShape = getResources().getDrawable(
R.drawable.shape_droptarget);
Drawable normalShape = getResources().getDrawable(R.drawable.shape);

@Override
public boolean onDrag(View v, DragEvent event) {
int action = event.getAction();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
// do nothing
break;
case DragEvent.ACTION_DRAG_ENTERED:
v.setBackgroundDrawable(enterShape);
break;
case DragEvent.ACTION_DRAG_EXITED:
v.setBackgroundDrawable(normalShape);
break;
case DragEvent.ACTION_DROP:
// Dropped, reassign View to ViewGroup
View view = (View) event.getLocalState();
ViewGroup owner = (ViewGroup) view.getParent();
owner.removeView(view);
LinearLayout container = (LinearLayout) v;
container.addView(view);
view.setVisibility(View.VISIBLE);
break;
case DragEvent.ACTION_DRAG_ENDED:
v.setBackgroundDrawable(normalShape);
default:
break;
}
return true;
}
}

It was though better than Motion Event Listener, in terms of smooth movement of Views but it couln’t solve the rest of the problems of Motion Event Listener.

The day passed with little succes with Drag and Drop. Let’s see if I could solve its issues.

Motion Event Listener

Today, I tried using Motion Event Listener. First, I read its complete documentation. This is very old API, came into picture in API 1 only.

Documentation is quite tough to understand. So I found some sources  which can help anyone to understand the working of this API-

I added the following code to see if this API is of any help-


public class Floormap extends Activity implements View.OnTouchListener {

TextView _view;
ViewGroup _root;
private int _xDelta;
private int _yDelta;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_root = (ViewGroup)findViewById(R.id.root);

_view = new TextView(this);
_view.setText("TextView!!!!!!!!");

RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(150, 50);
layoutParams.leftMargin = 50;
layoutParams.topMargin = 50;
layoutParams.bottomMargin = -250;
layoutParams.rightMargin = -250;
_view.setLayoutParams(layoutParams);

_view.setOnTouchListener(this);
_root.addView(_view);
}

public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
RelativeLayout.LayoutParams lParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_MOVE:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) view.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
layoutParams.rightMargin = -250;
layoutParams.bottomMargin = -250;
view.setLayoutParams(layoutParams);
break;
}
_root.invalidate();
return true;
}}

I tried this code using one image view only and it worked to some extent. The image was movable but there are some problems-

  • Image size gets reduced when moved towards the boundaries of application.
  • The movement of the view is not smooth.
  • The view getting out of the screen (from Left and Top sides).

I will work on this, if these problems could be solved. But, alongside I will start with Drag and Drop API.

The Tough Task

Now, we have to work on Floormap Activity. Our task is to provide the admin of the app the feature to arrange the tables in his restaurant for his clear understanding while forming the merge combinations of tables. Basically, in the app we have to show images of tables(in a particular category), which we can move here and there according to the actual arrangement of tables in that category.

It is quite difficult task since neither I and my fellow are aware of View movements in android. After lot of search,  we found two APIs –

We don’t have any idea about these APIs. We have to gain deeper understanding of these APIs and then try those in our app.

Let’s see if any of these APIs serve our purpose. 🙂

Getting familiar with gson

Till now, it was really hectic to fetch all column values from PHP and then map to corresponding attributes in the JAVA MODEL. But, the library which I came across today, saves us from this hectic task.

This is GSON library. To use it in the app, write the dependency as follows-

dependencies {
    compile 'com.google.code.gson:gson:2.8.2'
}

 

In JAVA:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.serializeNulls();
Gson gson = gsonBuilder.create();
array = jsonObject.getJSONArray("tables");
tableObject.addAll(Arrays.asList(gson.fromJson(String.valueOf(array), Table[].class)));

 

Using AdjustableImageView

Today, my task was to create Category Description Activity. There I had the functionality of image uploading and adding the description of category to be added by Admin. This is how it looks like.

category_desc.png

After lot of search, I found a fabulous library Nuuneoi AdjustableImageView. This library provides AdjustableImageView and AdjustableImageButton to correct the adjustViewBounds behavior of ImageView and ImageButton when run on API Level 17 and below.

To use this, add in dependencies-

dependencies {
    compile 'com.inthecheesefactory.thecheeselibrary:adjustable-imageview:1.0.1'
}

In the XML code, instead of ImageView, use this-

<com.inthecheesefactory.thecheeselibrary.widget.AdjustableImageView
    android:layout_width="300dp"
    android:layout_height="250dp"
    android:layout_gravity="center"
    android:layout_marginTop="15dp"
    android:layout_marginBottom="15dp"
    android:layout_marginRight="15dp"
    android:scaleType="center"
    android:adjustViewBounds="true"
    android:id="@+id/cImg"/>

 

Android Swipe Layout

Today, I came across the powerful Swipe layout Daimajia Swipe Layout.  I implemented the tables update functionality using this library only.

This is the screenshot of the activity where I used this library for showing update option.

list_tables_side

To use this library, add in dependencies:

dependencies {
    compile 'com.android.support:recyclerview-v7:21.0.0'
    compile 'com.android.support:support-v4:20.+'
    compile "com.daimajia.swipelayout:library:1.2.0@aar"
}

The layout in of table list item, using this library.

<?xml version="1.0" encoding="utf-8"?>
<com.daimajia.swipe.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:swipe="http://schemas.android.com/apk/res-auto"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/swipeTables"
    swipe:rightEdgeSwipeOffset="0dp">

    <LinearLayout
        android:id="@+id/bottom_wrapper_table"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:weightSum="1">

        <android.support.v7.widget.AppCompatImageView
            android:id="@+id/btnEditTable"
            app:srcCompat="@drawable/edit_category"
            android:layout_width="0dp"
            android:layout_height="70dp"
            android:layout_weight="1"
            android:clickable="true"
            android:adjustViewBounds="true"
            android:layout_gravity="center"
            android:paddingRight="20dp"
            android:paddingLeft="20dp"
            android:scaleType="fitCenter" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="20dp"
        android:paddingTop="20dp"
        android:paddingLeft="16dp"
        android:paddingRight="16dp"
        >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/txtTableName"
        android:maxLines="1"
        android:textColor="@color/light_black"
        android:gravity="left"
        android:textSize="16sp"
        android:text="Table Name"/>

    </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="@color/divider_color"/>

    </LinearLayout>

</com.daimajia.swipe.SwipeLayout>

 

In the adapter, write the code:

<pre>public void onBindViewHolder(final ListTablesAdapter.SimpleViewHolder viewHolder, final int position) {
viewHolder.swipeLayout.setShowMode(SwipeLayout.ShowMode.PullOut);

 // Drag From Left
 //viewHolder.swipeLayout.addDrag(SwipeLayout.DragEdge.Left, viewHolder.swipeLayout.findViewById(R.id.bottom_wrapper));
 viewHolder.swipeLayout.setRightSwipeEnabled(true);
 viewHolder.swipeLayout.setLeftSwipeEnabled(false);
 // Drag From Right
 viewHolder.swipeLayout.addDrag(SwipeLayout.DragEdge.Right, viewHolder.swipeLayout.findViewById(R.id.bottom_wrapper_table));


 // Handling different events when swiping
 viewHolder.swipeLayout.addSwipeListener(new SwipeLayout.SwipeListener() {
 @Override
 public void onClose(SwipeLayout layout) {
 //when the SurfaceView totally cover the BottomView.
 }

 @Override
 public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset) {
 //you are swiping.
 }

 @Override
 public void onStartOpen(SwipeLayout layout) {

 }

 @Override
 public void onOpen(SwipeLayout layout) {
 //when the BottomView totally show.
 }

 @Override
 public void onStartClose(SwipeLayout layout) {

 }

 @Override
 public void onHandRelease(SwipeLayout layout, float xvel, float yvel) {
 //when user's hand released.
 }
 });

 // mItemManger is member in RecyclerSwipeAdapter Class
 mItemManger.bindView(viewHolder.itemView, position);
}
@Override
public int getSwipeLayoutResourceId(int position) {
    return R.id.swipeTables;
}</pre>

 

This library is great.  I am planning to use it for more purposes in the app.