Thursday, September 4, 2014

Insert view to GridLayout dynamically, with even width and height

This example insert our custom view to a GridLayout (Added in API level 14) dynamically in Java code, and set the width of height evenly.


Modify layout, activity_main.xml, to have a GridLayout without child.
<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidtouchview.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <GridLayout
        android:id="@+id/mygrid"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:columnCount="8"
        android:rowCount="5"
        android:background="@android:color/background_light" >
    </GridLayout>

</LinearLayout>

MyView.java
package com.example.androidtouchview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View {
 
 public interface OnToggledListener {
  void OnToggled(MyView v, boolean touchOn);
 }

 boolean touchOn;
 boolean mDownTouch = false;
 private OnToggledListener toggledListener;
 int idX = 0; //default
 int idY = 0; //default

 public MyView(Context context, int x, int y) {
  super(context);
  idX = x;
  idY = y;
  init();
 }
 
 public MyView(Context context) {
  super(context);
  init();
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }

 private void init() {
  touchOn = false;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
    MeasureSpec.getSize(heightMeasureSpec));
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if (touchOn) {
   canvas.drawColor(Color.RED);
  } else {
   canvas.drawColor(Color.GRAY);
  }
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  super.onTouchEvent(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
             
             touchOn = !touchOn;
       invalidate();
       
       if(toggledListener != null){
        toggledListener.OnToggled(this, touchOn);
       }
             
                mDownTouch = true;
                return true;

            case MotionEvent.ACTION_UP:
                if (mDownTouch) {
                    mDownTouch = false;
                    performClick();
                    return true;
                }
        }
        return false;
 }

 @Override
 public boolean performClick() {
        super.performClick();
        return true;
 }
 
 public void setOnToggledListener(OnToggledListener listener){
  toggledListener = listener;
 }
 
 public int getIdX(){
  return idX;
 }
 
 public int getIdY(){
  return idY;
 }

}

MainActivity.java
Our custom views, MyView, are created dynamically and add to the GridLayout in onCreate(). Then re-align their LayoutParams in OnGlobalLayoutListener(), will be called when the global layout state or the visibility of views within the view tree changes.
package com.example.androidtouchview;

import com.example.androidtouchview.MyView.OnToggledListener;

import android.support.v7.app.ActionBarActivity;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.GridLayout;
import android.widget.Toast;
import android.os.Bundle;

public class MainActivity extends ActionBarActivity 
 implements OnToggledListener{
 
 MyView[] myViews;
 
 GridLayout myGridLayout;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  myGridLayout = (GridLayout)findViewById(R.id.mygrid);
  
  int numOfCol = myGridLayout.getColumnCount();
  int numOfRow = myGridLayout.getRowCount();
  myViews = new MyView[numOfCol*numOfRow];
  for(int yPos=0; yPos<numOfRow; yPos++){
   for(int xPos=0; xPos<numOfCol; xPos++){
    MyView tView = new MyView(this, xPos, yPos);
    tView.setOnToggledListener(this);
    myViews[yPos*numOfCol + xPos] = tView;
    myGridLayout.addView(tView);
   }
  }
  
  myGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(
   new OnGlobalLayoutListener(){

   @Override
   public void onGlobalLayout() {
    
    final int MARGIN = 5;
    
    int pWidth = myGridLayout.getWidth();
    int pHeight = myGridLayout.getHeight();
    int numOfCol = myGridLayout.getColumnCount();
    int numOfRow = myGridLayout.getRowCount();
    int w = pWidth/numOfCol;
    int h = pHeight/numOfRow;
    
    for(int yPos=0; yPos<numOfRow; yPos++){
     for(int xPos=0; xPos<numOfCol; xPos++){
      GridLayout.LayoutParams params = 
       (GridLayout.LayoutParams)myViews[yPos*numOfCol + xPos].getLayoutParams();
      params.width = w - 2*MARGIN;
      params.height = h - 2*MARGIN;
      params.setMargins(MARGIN, MARGIN, MARGIN, MARGIN);
      myViews[yPos*numOfCol + xPos].setLayoutParams(params);
     }
    }

   }});
 }

 @Override
 public void OnToggled(MyView v, boolean touchOn) {

  //get the id string
  String idString = v.getIdX() + ":" + v.getIdY();

  Toast.makeText(MainActivity.this, 
   "Toogled:\n" +
   idString + "\n" +
   touchOn, 
   Toast.LENGTH_SHORT).show();
 }

}

download filesDownload the files.

Notice:
- OnGlobalLayoutListener will be called repeatly this way. To remove it, call removeGlobalOnLayoutListener() or removeOnGlobalLayoutListener(), read next post.



4 comments:

Anonymous said...

Add some comments in codes explaining what is doing what.. Otherwise nice article in fact nice blog but having problem understanding few things, specially for person who coming from non programming background. Hope you will consider this.

fathur said...

thank you :D

ganco said...

Really Thanks!!!!! Finally I can apply margins to textviews in gridlayout programmatically (not padding)
and I realize once again that I really hate this xxxx android API...

275eaniversarisantana said...

I misstyped the link of StackOverflow, that's it --> http://stackoverflow.com/questions/36063659/gridlayout-with-view-dynamic-get-row-column thanks a lot man...