Friday, March 7, 2014

How to make an image with clickable areas with pinch/zoom and move effects

First I need to mention that I got this code samples from below articles and all the credits should go to these authors.

1 . Android Images with Clickable Areas - Part 1 by Bill Lahti

2 . Java Pan / Zoom listener for Android by John Stewien

From first article you can learn how to make an image with clickable areas very easily and from the second article you can learn how to get the pan/zoom effect to an image.

I just combined these two ideas and created an application which has both features. (Image with clickable and at the same time it can be zoomed as well)

I am going to explain only the changes I did for those codes. I mention the name of the java file with codes for your easy reference.You can find the link to source code at the bottom of this article.

PanAndZoomListener class declaration  : PanAndZoomListener extends View.OnTouchListener

(1) Since Images with Clickable areas have two images and we need to zoom both images at the same time I passed both ImageViews as a View array to PanAndZoomListener constructor. Also we need to pass the Context of the Activity class as well. Here is the code,

Inside PanAndZoomListener.java

public PanAndZoomListener(FrameLayout containter, View[] views, int anchor,Context c) {
        panZoomCalculator = new PanZoomCalculator(containter, views, anchor);

        //iv_front and iv_back are ImageViews declared as instance variables.
         iv_front = (ImageView) views[0];
         iv_back = (ImageView)views[1];
         context = c;
         iv_front.setOnTouchListener(this);
 }

(2) Now we need to change the panZoomCalculator constructor accordingly. Because earlier it accepted one View. But now we are passing a View array. So lets change it like this,

Inside PanAndZoomListener.java

PanZoomCalculator(View container, View[] child, int anchor) {
     // Initialize class fields
     currentPan = new PointF(0, 0);
     currentZoom = 1f;

     this.window = container;
     this.child1 = child[0];
     this.child2 = child[1];

     matrix = new Matrix();
     this.anchor = anchor;
     onPanZoomChanged();

    this.child1.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
   
    public void onLayoutChange(View v, int left, int top, int right, int                 bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    onPanZoomChanged();
         }
    });

   this.child2.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {

   public void onLayoutChange(View v, int left, int top, int right, int                    bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    onPanZoomChanged();
       }
   });
}

(3) Then modify onPanZoomChanged() method like this,
you need to create another if-else block for child2

Inside PanAndZoomListener.java

if (child1 instanceof ImageView && ((ImageView) child1).getScaleType() == ImageView.ScaleType.MATRIX) {


 ImageView view = (ImageView) child1;
 Drawable drawable = view.getDrawable();
  if (drawable != null) {
   Bitmap bm = ((BitmapDrawable) drawable).getBitmap();
   if (bm != null) {
      float bmWidth = bm.getWidth();
      float bmHeight = bm.getHeight();

      float fitToWindow = Math.min(winWidth / bmWidth, winHeight / bmHeight);
      float xOffset = (winWidth - bmWidth * fitToWindow) * 0.5f * currentZoom;
      float yOffset = (winHeight - bmHeight * fitToWindow) * 0.5f * currentZoom;

      matrix.reset();
      matrix.postScale(currentZoom * fitToWindow, currentZoom * fitToWindow);
      matrix.postTranslate(currentPan.x + xOffset, currentPan.y + yOffset);
      ((ImageView) child1).setImageMatrix(matrix);
  }
 }
}else {
        ViewGroup.MarginLayoutParams lp =                (ViewGroup.MarginLayoutParams)child1.getLayoutParams();

         lp.leftMargin = (int) currentPan.x + panJitter;
         lp.topMargin = (int) currentPan.y;
         lp.width = (int) (window.getWidth() * currentZoom);
         lp.height = (int) (window.getHeight() * currentZoom);
         panJitter ^= 1;

         child1.setLayoutParams(lp);

}

(4) Inside onTouch() we need to add the code for what to happen when a user is pressed a particular area on the image. It should be triggered when MotionEvent.ACTION_UP event has occurred.

Inside PanAndZoomListener.java

case MotionEvent.ACTION_UP: {
 if (view.getId() == iv_front.getId()) {

  final int x = (int) event.getX();
  final int y = (int) event.getY();
  int touch_color = getHotspotColor(x, y);
  int tolerance = 25;

  if (closeMatch(Color.BLUE, touch_color, tolerance)) 
    Toast.makeText(context, "Pressed Blue color box", Toast.LENGTH_SHORT).show();
  else if (closeMatch(Color.RED, touch_color, tolerance)) 
    Toast.makeText(context, "Pressed Red color box", Toast.LENGTH_SHORT).show();
  else if (closeMatch(Color.GREEN, touch_color, tolerance)) 
   Toast.makeText(context, "Pressed Green color box", Toast.LENGTH_SHORT).show();
  else if (closeMatch(Color.YELLOW, touch_color, tolerance)) 
  Toast.makeText(context, "Pressed Yellow color box", Toast.LENGTH_SHORT).show();
 }
}

(5) Now inside our onCreate() of  the Activity class, we need to initialize the views and make a new PanAndZoomListener object and pass the values like this,

Inside MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  FrameLayout fl = (FrameLayout)findViewById(R.id.frame_layout);
  iv_front = (ImageView)findViewById(R.id.pan_and_zoom_image);
  iv_back = (ImageView)findViewById(R.id.pan_and_zoom_image_areas);
  View[] views = {iv_front,iv_back};
  fl.setOnTouchListener(new PanAndZoomListener(fl, views, PanAndZoomListener.Anchor.TOPLEFT,MainActivity.this));
}


That is it. 

Here is the source code (I used Android Studio, min sdk =11)

In my source code I used the below image. When you pressed the  tables at right side  toast messages will be appeared accordingly.











Wednesday, March 5, 2014

Migrating from Eclipse to Android Studio

I had a very difficult time to import an eclipse project to Android Studio.  So finally I found a method to do that.

Step 1 : Open up eclipse and export the project that need to import to Android Studio.


Step 2 : A window will be appeared. Select "Generate Gradle build files"


Step 3: Select the project you want to export


Step 4 : Now a window will be appeared like below. Click "Finish" button.


Step 5 :  Again click "Finish" in next window as well.


Step 6 : Now if all the things worked fine, a file named "build.gradle" has been generated inside your selected project folder.


Step 7 : This is the gradle version that is used to build the project. Open that build.gradle file to check your gradle version.



Step 8 :  Now open up the Android Studio. Select File->Open. Select that build.gradle  file. Press "Ok"



Step 9 : Then a message box will pop up like this. Select "Yes"


Step 10 : Now a new window will be appeared like below. I have installed gradle 1.9 version on my PC. So I select "Use local gradle distribution" option. If you want you can download  latest gradle version from here. Also you can select the "recommended" option as well.




Step 10 : Select "Ok". It will start to build the project and finally import into Android Studio.



Here is the final output.


Other references : https://www.youtube.com/watch?v=b84t7p9YuX8&index=11&list=LL7iK14mNE71scGEhxKuWNlw

Friday, February 28, 2014

How to set an activity dynamically for up-button in android

This is done by implementing getSupportParentActivityIntent() method  and using it we can dynamically set the activity to up-button in android. Here how I have achieved it.

Assume we have two activities. Activity A and B. A is the parent activity and B is the child.

So A need to create an intent to start B. It should pass an extra data which is the name of the parent activity. Here in our example it should be 'A'. Here is the code,

    Intent intent = new Intent();
    intent.putExtra("ParentClassName","A");
    startActivity(intent.setClass(A.this, B.class)); //we are starting activity 'B'

Now in activity B we need to override getSupportParentActivityIntent() and it should look like this,

     @Override
     public Intent getSupportParentActivityIntent() {
           Intent parentIntent= getIntent();

           //getting the parent class name
           String className = parentIntent.getStringExtra("ParentClassName");

           Intent newIntent=null;

           try {
                //you need to define the class with package name
                newIntent = new Intent(B.this,Class.forName("com.myapplication."+className));
          } catch (ClassNotFoundException e) {
                e.printStackTrace();
           }
           return newIntent;
      }

Sunday, February 9, 2014

Hello everyone and Welcome to my blog

I am Chathura De Silva. I had a thought of writing a blog to post about interested and new things that I have learnt during my work (which means developing applications). As I am interested on mobile and web technologies and those areas have high demand in the industry, I spend most of my time to learn them and I use this blog to post the things such as solutions to the problems that I have encountered, methods to implement various things with code samples, new trends/developments in the field, new apps etc. I do not target any particular audience here and I hope to use this blog as a reference to me. But anyone can free to comment to posts or share knowledge which makes a good time for all of us.