Wednesday, June 20, 2012

Android: Bigger image for first item of the gridview

Say you want layout like below:

I start to implement with following XML , activity, adapter:



<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

        <GridView
            android:id="@+id/gridView1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:numColumns="5" >
        </GridView>

</LinearLayout>



package demo.grid.view;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

public class DemoGridViewActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        GridView gridView = (GridView)findViewById(R.id.gridView1);
        gridView.setAdapter(new GridViewAdaptor());
    }
 
    class GridViewAdaptor extends BaseAdapter{

@Override
public int getCount() {
return 50;
}

@Override
public Object getItem(int arg0) {
return arg0;
}

@Override
public long getItemId(int arg0) {
return arg0;
}

@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
ImageView imageView;
if(arg1==null){
imageView = new ImageView(DemoGridViewActivity.this){
@Override
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}
};
}else{
imageView = (ImageView) arg1;
}

imageView.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
imageView.setBackgroundColor(Color.BLUE);
imageView.setScaleType(ScaleType.FIT_XY);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
switch(arg0){
case 0:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.RED);
return imageView;
case 1:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, bitmap.getWidth()/2, 0, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.GREEN);
return imageView;
case 5:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.YELLOW);
return imageView;
case 6:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.MAGENTA);
return imageView;
default:
imageView.setImageResource(R.drawable.ic_launcher);
return imageView;
}
}
   
    }
}



Now this issue is how to work with a list of object and translate it into this grid view. Following translation needed to be done for adapter:

@Override
public int getCount() {
return integers.size()+3;
}


switch(arg0){
case 0:
imageView.setTag(integers.get(0));

case 1:
imageView.setTag(integers.get(0));
case 5:
imageView.setTag(integers.get(0));

case 6:
imageView.setTag(integers.get(0));
default:
if(arg0>1 && arg0<=4){
imageView.setTag(integers.get(arg0-1));
}else{
imageView.setTag(integers.get(arg0-3));
}
}

full code is here:

package demo.grid.view;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

public class DemoGridViewActivity extends Activity {
    /** Called when the activity is first created. */


List integers = new ArrayList();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
     
        for(int i = 0 ; i < 50 ; i++){
        integers.add(i);
        }
     
        GridView gridView = (GridView)findViewById(R.id.gridView1);
        gridView.setAdapter(new GridViewAdaptor());
    }
 
    class GridViewAdaptor extends BaseAdapter{

@Override
public int getCount() {
return integers.size()+3;
}

@Override
public Object getItem(int arg0) {
return arg0;
}

@Override
public long getItemId(int arg0) {
return arg0;
}

@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
ImageView imageView;
if(arg1==null){
imageView = new ImageView(DemoGridViewActivity.this){
@Override
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setTextSize(36);
canvas.drawText(getTag().toString(), getWidth()/2, getHeight()/2, paint);
}
};
}else{
imageView = (ImageView) arg1;
}

imageView.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
imageView.setBackgroundColor(Color.BLUE);
imageView.setScaleType(ScaleType.FIT_XY);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
switch(arg0){
case 0:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.RED);
imageView.setTag(integers.get(0));
return imageView;
case 1:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, bitmap.getWidth()/2, 0, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.GREEN);
imageView.setTag(integers.get(0));
return imageView;
case 5:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.YELLOW);
imageView.setTag(integers.get(0));
return imageView;
case 6:
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.MAGENTA);
imageView.setTag(integers.get(0));
return imageView;
default:
if(arg0>1 && arg0<=4){
imageView.setTag(integers.get(arg0-1));
}else{
imageView.setTag(integers.get(arg0-3));
}

imageView.setImageResource(R.drawable.ic_launcher);
return imageView;
}
}
   
    }
}



finally this code for loading images from device sdcard and display in the gridview:





package demo.grid.view;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.MediaStore.Images.Thumbnails;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
public class DemoGridViewActivity extends Activity {
    private static final int START_PROGRESS = 10;
List mBitmaps = new ArrayList();
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        AsyncTask asyncTask = new AsyncTask(){
        @Override
        protected void onPreExecute() {
        showDialog(START_PROGRESS);
        super.onPreExecute();
        }
@Override
protected Void doInBackground(Void... params) {
Cursor query = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media._ID}, null, null, null);
if(query!=null && query.moveToFirst()){
do{
int columnIndex = query.getColumnIndex(MediaStore.Images.Media._ID);
long origId = query.getLong(columnIndex);
mBitmaps.add(MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(), origId, Thumbnails.MICRO_KIND, null));
}while(query.moveToNext());
query.close();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
removeDialog(START_PROGRESS);
       GridView gridView = (GridView)findViewById(R.id.gridView1);
       gridView.setAdapter(new GridViewAdaptor());
super.onPostExecute(result);
}
        };
        asyncTask.execute();
    }
    @Override
    protected Dialog onCreateDialog(int id, Bundle args) {
    return new ProgressDialog(this);
    }
    class GridViewAdaptor extends BaseAdapter{
@Override
public int getCount() {
return mBitmaps.size()+3;
}
@Override
public Object getItem(int arg0) {
return arg0;
}
@Override
public long getItemId(int arg0) {
return arg0;
}
@Override
public View getView(int arg0, View arg1, ViewGroup arg2) {
ImageView imageView;
if(arg1==null){
imageView = new ImageView(DemoGridViewActivity.this){
@Override
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
}
};
}else{
imageView = (ImageView) arg1;
}
imageView.setLayoutParams(new GridView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
imageView.setBackgroundColor(Color.BLUE);
imageView.setScaleType(ScaleType.FIT_XY);

Bitmap bitmap;
switch(arg0){
case 0:
imageView.setPadding(10, 10, 0, 0);
bitmap = mBitmaps.get(0);
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.RED);
return imageView;
case 1:
imageView.setPadding(0, 10, 10, 0);
bitmap = mBitmaps.get(0);
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, bitmap.getWidth()/2, 0, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.GREEN);
return imageView;
case 5:
imageView.setPadding(10, 0, 0, 10);
bitmap = mBitmaps.get(0);
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, 0, bitmap.getHeight()/2, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.YELLOW);
return imageView;
case 6:
imageView.setPadding(0, 0, 10, 10);
bitmap = mBitmaps.get(0);
imageView.setImageBitmap(Bitmap.createBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, bitmap.getWidth()/2, bitmap.getHeight()/2));
imageView.setBackgroundColor(Color.MAGENTA);
return imageView;
default:
if(arg0>1 && arg0<=4){
bitmap = mBitmaps.get(arg0-1);
}else{
bitmap = mBitmaps.get(arg0-3);
}
imageView.setPadding(10, 10, 10, 10);
imageView.setImageBitmap(bitmap);
return imageView;
}
}
    }
}



here you have sample code: source code

please check this post for more update: http://sudarnimalan.blogspot.sg/2012/06/android-bigger-image-for-any-of-image.html

Tuesday, June 19, 2012

Part1 : Useful Eclipse Templates for Android Development

I normally setup eclipse templates for repeat coding fragments. If you are not aware of eclipse editor templates, just a small example: just open a java code file and type "syso" (shortcut name) and press Ctrl+space then you will see all  "System.out.println();" will be inserted by eclipse for you.
To see all existing templates and its shortcut open your eclipse, open the window menu and click on Preference. Inside the Preference dialog , navigate to Java --> Editor --> Templates, Now you see full list there. (Eclipse-->Window-->Preference-->Java-->Editor-->Templates)
You can add your own templates there and make use of them in your coding and make your coding fast. Just click "New.." button, fill the "New Template" Dialog and OK.

1. In android, in case if we want to get logs we want to insert "Log.i" statements with proper message. This template will be useful for that.

Name: alog
Context: Java statements
Description: android log statement
Pattern: Log.i(TAG,"::${enclosing_method}:"+"${cursor}");

Now,For Example, in side onCreate  if you type alog and press Ctrl+space you will get:
Log.i(TAG, "::onCreate:" + ""); 

2. In #1 you see, we have to pass a TAG, normally this should be the Class name of the method we define in the top. I used to go for a full qualified name of any class. Sometime this will be useful in case of Custom Views to copy its name to crate layout XML files.


Name: atag
Context: Java type members
Description: android log tag
Pattern: 

@SuppressWarnings("unused")
private static final String TAG = "${enclosing_package}.${enclosing_type}";


Ex: @SuppressWarnings("unused")
private static final String TAG = "demo.DemoAcitity";


3. Inside the activity most we have to find View By Id and call some method on it.


Name: afind
Context: Java
Description: android find view
Pattern: 

${type} ${new_name} = (${type})findViewById(R.id.${cursor});

type afind and press Ctrl+Space, then you will get:
type| new_name = (type)findViewById(R.id.);

for example if that is a button then, type Button or But and press Ctrl+Space then you will get:
Button new_name = (Button)findViewById(R.id.);

Then press Tab cursor will move to new_name
Button new_name = (Button)findViewById(R.id.);

Type the name you want for the variable, then press Tab cursor will move after ".id.":
Button button = (Button)findViewById(R.id.|);
The just press Ctrl+space and choose the id from the list.

So you will end as follows:
Button button = (Button)findViewById(R.id.btn_add);

In android we mostly do this in onCreate method and assign to member variable. and refer from other method. Now you right click on the variable name choose Refactor  and choose "Convert Local Variable to Field" this will create a Field for you.


here i have uploaded the template file.

Part 2 : Useful Eclipse Templates for Android Development

Monday, June 18, 2012

Android: LinearLayout, Dynamically Arranging Views

Let see how to arrange buttons according to the user selection as shown below:


<LinearLayout android:orientation="vertical" android:id="@+id/linearLayout1"
     android:layout_height="fill_parent" android:layout_width="fill_parent">
    <Button android:text="Button" android:id="@+id/button0"
          android:layout_height="fill_parent" android:layout_width="fill_parent"
          android:layout_weight="1">Button>
     <Button android:text="Button" android:id="@+id/button1"
          android:layout_height="fill_parent" android:layout_width="fill_parent"
          android:layout_weight="1" android:visibility="gone">Button>
     <Button android:text="Button" android:id="@+id/button2"
          android:layout_height="fill_parent" android:layout_weight="1"
          android:layout_width="fill_parent" android:visibility="gone">Button>
LinearLayout>


Following java code would change the visibility according to the user selection.

@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
     switch(group.getCheckedRadioButtonId()){
     case R.id.radio0:
          button0.setVisibility(View.VISIBLE);
          button1.setVisibility(View.GONE);
          button2.setVisibility(View.GONE);
          break;
     case R.id.radio1:
          button0.setVisibility(View.VISIBLE);
          button1.setVisibility(View.VISIBLE);
          button2.setVisibility(View.GONE);
          break;
     case R.id.radio2:
          button0.setVisibility(View.VISIBLE);
          button1.setVisibility(View.VISIBLE);
          button2.setVisibility(View.VISIBLE);
          break
      }
}

Let see if we want a layout to change as show below:
This can be down in two ways.
Option 01: changing the layout XML file to use the weight sum:

<LinearLayout android:orientation="vertical" android:id="@+id/linearLayout1"
     android:layout_height="fill_parent" android:layout_width="fill_parent" android:weightSum="3">
     <Button android:text="Button" android:id="@+id/button0"
          android:layout_width="fill_parent"
          android:layout_weight="1" android:layout_height="0dip">Button>
     <Button android:text="Button" android:id="@+id/button1"
          android:layout_width="fill_parent"
          android:layout_weight="1" android:visibility="gone" android:layout_height="0dip">        Button>
     <Button android:text="Button" android:id="@+id/button2"
          android:layout_weight="1"
          android:layout_width="fill_parent" android:visibility="gone" android:layout_height="0dip">Button>
LinearLayout>

Option 02: using java code:

@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
     switch(group.getCheckedRadioButtonId()){
     case R.id.radio0:
          button0.setVisibility(View.VISIBLE);
          button1.setVisibility(View.INVISIBLE);
          button2.setVisibility(View. INVISIBLE);
          break;
     case R.id.radio1:
          button0.setVisibility(View.VISIBLE);
          button1.setVisibility(View.VISIBLE);
          button2.setVisibility(View. INVISIBLE);
          break;
     case R.id.radio2:
          button0.setVisibility(View.VISIBLE);
          button1.setVisibility(View.VISIBLE);
          button2.setVisibility(View.VISIBLE);
          break
      }
}

option 01 you should change the xml file and in option 02 you should change the java code.

Saturday, May 19, 2012

Android: Expand Liner Layout Beyond its Width

In android, the layout weight parameter in the liner layout is very useful to layout views with relative ratios. Say you want to arrange two frames left and right, and right frame take quoter and right take the balance.


We can simply create this using following layout xml. Only thing is to noted down is, the width should be set to 0dip and weight to be set to 0.25 or 0.75 according to the requirement. That all.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"
    android:weightSum="1" >

    <FrameLayout
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="0.75"
        android:background="#FF0000" >
    </FrameLayout>

    <FrameLayout
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.25"
        android:background="#00FF00" >

        <ToggleButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
    </FrameLayout>

</LinearLayout>


But in our case, we wanted to add another frame next to the right frame its width also 0.75 of the screen and in some user action, by moving the screen, the left frame should be hidden and 3rd frame should be shown.



So we created custom liner layout by extending the liner layout, where we override the on measure method to adjust its size by 0.75 of its size.


public class ExpandedLinearLayout extends LinearLayout {
@SuppressWarnings("unused")
private static final String TAG = "demo.beyond.layout.ExpandedLinearLayout";
public ExpandedLinearLayout(Context context) {
super(context);
}
public ExpandedLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExpandedLinearLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension((int) (1.75f*getMeasuredWidth()), getMeasuredHeight());
}
}



And add another new frame to above layout with weight 0.75.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
.................    
    <FrameLayout
        android:layout_width="0dip"
        android:layout_height="match_parent"
        android:layout_weight="0.75"
        android:background="#0000FF" >
    </FrameLayout>

</LinearLayout>


Then we add the animation for the on click.


public class DemoExpandLayoutActivity extends Activity implements OnClickListener {
        private LinearLayout mLinearLayout;
private FrameLayout mFrameLayout1;
private ToggleButton mToggleButton;
/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mLinearLayout = (LinearLayout)findViewById(R.id.frame);
        mFrameLayout1 = (FrameLayout)findViewById(R.id.frameLayout1);
        mToggleButton = (ToggleButton)findViewById(R.id.toggleButton1);
        mToggleButton.setOnClickListener(this);
    }

@Override
public void onClick(View arg0) {
float factor = 1.0f*mFrameLayout1.getWidth()/mLinearLayout.getWidth();
if(mToggleButton.isChecked()){
mLinearLayout.setTranslationX(-mFrameLayout1.getWidth());
TranslateAnimation translateAnimation = new TranslateAnimation(
TranslateAnimation.RELATIVE_TO_PARENT,
factor,
TranslateAnimation.RELATIVE_TO_PARENT,
0.0f,
TranslateAnimation.RELATIVE_TO_PARENT,
0.0f,
TranslateAnimation.RELATIVE_TO_PARENT,
0.0f);
translateAnimation.setDuration(1000);
LayoutAnimationController layoutAnimationController = new LayoutAnimationController(translateAnimation,0.1f);
mLinearLayout.setLayoutAnimation(layoutAnimationController);
}else{
mLinearLayout.setTranslationX(0);
TranslateAnimation translateAnimation = new TranslateAnimation(
TranslateAnimation.RELATIVE_TO_PARENT,
-factor,
TranslateAnimation.RELATIVE_TO_PARENT,
0.0f,
TranslateAnimation.RELATIVE_TO_PARENT,
0.0f,
TranslateAnimation.RELATIVE_TO_PARENT,
0.0f);
translateAnimation.setDuration(1000);
LayoutAnimationController layoutAnimationController = new LayoutAnimationController(translateAnimation,-0.1f);
mLinearLayout.setLayoutAnimation(layoutAnimationController);
}
}
}


But that didn't quit worked out, because liner layout ignores views If those out side the weight sum.

But if we add views as match patent, with layout weights, liner layout behaves differently. It draws all the views beyond its boundary.
In this case the actual width of each view was not proportional to layout weight what we set in layout XML.
So to find out the relation, I just try for different values and plot the following graph and got the below equation.



I couldn't find any direct evidence in android code base or may have to check some more android source code. But this is good enough to calculate the proper values for us.

Then I changed the layout XML with those values.


<?xml version="1.0" encoding="utf-8"?>
<demo.beyond.layout.ExpandedLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/frame"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"
    android:weightSum="1" >

    <FrameLayout
        android:id="@+id/frameLayout1"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.125"
        android:background="#FF0000" >
    </FrameLayout>

    <FrameLayout
        android:id="@+id/frameLayout2"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.375"
        android:background="#00FF00" >

        <ToggleButton
            android:id="@+id/toggleButton1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
    </FrameLayout>

    <FrameLayout
        android:id="@+id/frameLayout3"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:layout_weight="0.125"
        android:background="#0000FF" >
    </FrameLayout>

</demo.beyond.layout.ExpandedLinearLayout>


Further more I modified the expanded liner layout with this formula, so that it can use in all cases.


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int numberOfChild = getChildCount();
float totalWidth = 0;
for(int i = 0;i < numberOfChild;i++){
totalWidth+=getMeasuredWidth()
*(1 - 2*((LinearLayout.LayoutParams)getChildAt(i).getLayoutParams()).weight);
}
setMeasuredDimension((int) totalWidth, getMeasuredHeight());
}


Here I have uploaded the source code, try it your self and let me know your feedback or let me know if you have any other option.





Tuesday, May 15, 2012

Android: How to Check if Media Scanner is Running?

In our application, some of the action need to be blocked while android media scanner running (For example loading existing media and listing down).

In this case, we couldn't find a direct API to check if Media Scanner is running. We could find following two options:
Option 01:
Android will broadcast Intent.ACTION_MEDIA_SCANNER_STARTEDIntent.ACTION_MEDIA_SCANNER_FINISHED in starting and finishing of scanning respectively. So we create following  BroadcastReceiver:

public class MediaScannerBroadcastReceiver extends BroadcastReceiver {
public static boolean mMedaiScanning = false;
        @Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_MEDIA_SCANNER_STARTED)){
mMedaiScanning = true;
}
if(intent.getAction().equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)){
mMedaiScanning = false;
}
}
}


And register this in manifest file:

<receiver android:name=" MediaScannerBroadcastReceiver">
     < intent-filter>

           < action android:name="android.intent.action.MEDIA_SCANNER_FINISHED" >  < /action>
          < action android:name="android.intent.action.MEDIA_SCANNER_STARTED" >  < /action>
           < data android:scheme="file" >  < /data>
      < /intent-filter>
</receiver>


Then in cases we we wanted to check the condition we sued as following:
if( MediaScannerBroadcastReceiver.mMedaiScanning){
     .....
}

But, most of the devices will run Media Scanner in boot up, in adding SDCard or mount/unmount devices to PC. In those cases our application is invoked and loaded into memory.

Option 02:
found that the media scanner service insert and delete a record into media store when starting and finishing the scanning. So we try to check the record in the media store as follows:

private boolean isMediaScannerRunning() {
Cursor query = getContentResolver().query(MediaStore.getMediaScannerUri(),
                                                   new String[]{MediaStore.MEDIA_SCANNER_VOLUME}, nullnullnull);
        if(query!=null){
         if(query.moveToFirst()){
         int columnIndex = query.getColumnIndex(MediaStore.MEDIA_SCANNER_VOLUME);
         String volumeName = query.getString(columnIndex);
         if(volumeName!=null){
         return true;
         }
         }
         query.close();
        }
        return false;
}

This option will not load the application while media scanner running. But need to consider its costly operation in query.

Lets check both options for its pros and cons.

Saturday, October 1, 2011

Part 7: Thoughts of “How we do?” - Reading from Near vs Far


Say you are traveling in a bus and trying to read a board on both sides of the road. First board is on the same side where you are traveling and the second board on the other side of the road. Both has same text. And the board on the opposite side has enough font size to read it from the bus which you are traveling. Normally we can only star reading once we come inside the visual angle of the board.

Assume you are reading both boards in two different situation from start to end while bus moving in same speed. You will understand that, you can read more from the board which is in the opposite side. So when we are traveling, without reducing our speed, we can't read more from near, rather from far we can. But the distance should be enough to identify the letters, that all.

Now, let see how we can apply this into a real life situations.

Team leaders, if your team members are currently working in a project with a constant speed. And you are hearing some new project opportunity. It is not good to wait until your project got confirm and then bring it to the members to their close attention. Because members have to slowdown their current project in order to prepare the new project. Instead of that, if you let them prepare the new project early as possible in a relax mode,  this will not much affect the current project speed.

Here one of the different between close attention and relax mode would be, in close attention team leader will expect and check for updates very frequently, rather in relax mode leader could wait until the team member bring updates to leaders' table or expect and check update occasionally.

One concern may be: change in the project detail will make the preparation waste, but that is not fully true. Because if we have already prepared and then you summaries the different from early draft we don't need much effect to override the new changes. If the project details totally change, then of cause all the initial preparation is waste. Mostly that not the case.

But for the success of relax mode approach, team member should have enough skill level. They should be proactive to prepare project in early stage, self motivated to prepare project and provide updates. They should be able manage and balance work load in preparation new project with the current project.

Feather more, letting the team member to prepare the project early will lead to understand any unclear or impossible areas in the project so that the project could be modify accordingly. This will help in project executions very much.

So, I feel it is better to do go for a early preparation of new project. Both team leaders and members should understand the pros and cons in this approach and should act accordingly. This will bring a best team and best product in the end.

Let me find out another real example as above and meet you another post. Bye 

Android : Drawing View with shapes plotted in r theta (r,θ) relation

r = 0.875*width/2+0.125cos(12θ)
r - width/2*cos(4θ)
r - width/2
Say you want to create views which contains shares as shown above. In those each circle drawn in (r,θ) relation. I implemented following onDraw method with possible two options.
Option 1. Rotating canvas and drawing circles - this is direct use of (r , θ)
Option 2. Converting points into x,y plan coordinate and drawing the circle.


@Override
protected void onDraw(Canvas canvas) {
float width = getWidth()/2;
float height = getHeight()/2;
Paint paint = new Paint();
paint.setColor(Color.RED);
        //Option 1
long time = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 13;i++){
canvas.drawCircle(width, 0, width*0.2f, paint);
canvas.rotate(-15);
}
canvas.restore();
Log.i(TAG, "::onDraw:" + "time 1 = " + (System.currentTimeMillis()-time));
        //Option 2
long time1 = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
float angle = 0;
for(int i = 0 ; i < 13;i++){
float radian = (float) (Math.PI*angle/180.0f);
canvas.drawCircle((float) (width*Math.cos(radian)),
(float) (width*Math.sin(radian)), width*0.2f, paint);
angle+=15;
}
canvas.restore();
Log.i(TAG, "::onDraw:" + "time 2 = " + (System.currentTimeMillis()-time1));
}

Since above coding draw only 13 circles, I couldn't find a clear different in execution time for both options. So I change the code to draw 180 circle as follows:


@Override
protected void onDraw(Canvas canvas) {
float width = getWidth()/2;
float height = getHeight()/2;
Paint paint = new Paint();
paint.setColor(Color.RED);
long time1 = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 180;i++){
float radius = width;
canvas.drawCircle(radius, 0, 2.0f, paint);
canvas.rotate(-1);
}
canvas.restore();
time1 = System.currentTimeMillis()-time1;
long time2 = System.currentTimeMillis();
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 180;i++){
float radian = (float) (Math.PI*i/180.0f);
float radius = width;
canvas.drawCircle((float) (radius*Math.cos(radian)),
(float) (radius*Math.sin(radian)), 2.0f, paint);
}
canvas.restore();
time2 = System.currentTimeMillis()-time2;
Log.i(TAG, "::onDraw:" + "time1,time2, (time1-time2) = "
+ time1 + ","+time2+","+(time1-time2));
}

Now i could see clear different and Option 2 is faster than Option 1.



INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 62,35,27
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 66,36,30
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 66,37,29
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 63,35,28
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 68,34,34
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 63,35,28
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 61,35,26
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 59,35,24
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 68,40,28
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 75,32,43
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 65,32,33
INFO/demo.theta.DemoRTheta(5302): ::onDraw:time1,time2, (time1-time2) = 52,36,16


Here you have expresions for other above graphs. 

float radian = (float) (Math.PI*i/180.0f);

float radius = width;
float radius = (float) (width*Math.cos(4*radian));
float radius = (float) (0.875f*width+0.125f*width*Math.cos(12*radian));



But Option 2 needs some extra calculation to translate  (r , θ) into (x, y), in above examples we needed to determine only center of the circle.
Let see some of the view as follows.
canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 72;i++){
canvas.drawLine(width*0.8F, 0, width*1.0f, 0, paint);
canvas.rotate(5);
}
canvas.restore();

Now we have to find start x,y and end x,y that need extra calculation and extra analysis effect to come up with that expresion.
Next one is more interesting need to use Path.

canvas.save();
canvas.translate(width, height);
for(int i = 0 ; i < 18;i++){
Path path = new Path();
path.moveTo(width*0.8F, 0);
path.lineTo(width, width*0.1F);
path.lineTo(width, -width*0.1F);
path.lineTo(width*0.8F, 0);
canvas.drawPath(path, paint);
canvas.rotate(20);
}
canvas.restore();

Now you can find translation expression and can find out which one is faster in each cases.
Enjoy!