Search This Blog
Thursday, May 6, 2010
Ebay-Search Upgraded
Version 0.2 of Ebay-Search application has been published, this version will enable users to do more sophisticated searches using different Ebay search Filters such as Condition, Listing type, Available Country , etc...
Monday, March 15, 2010
Android Layout Managers
Sometimes There are some simple rules and facts which can easily improve the quality and performance of your applications but they are widely ignored either because of lack of knowledge or just because of our reluctance to change the way we're already doing stuff.
One of these basic principles in android is Layout management which is hugely being misused, I mean if you go through different android samples and blogs (including this Blog!) more than 70% of all samples and examples are using LinearLayout even for some very simple layout that could have been simply done by using a FrameLayout. I am no exception and actually I used to think if I am comfortable with LinearLayout and everybody else is using it, why should I use another layout? but the thing is LinearLayout is a pretty heavyweight LayoutManager compare to FrameLayout and a far less flexible layout compare to RelativeLayout (this inflexibility usually leads to one of those ugly layout files with 4 or 5 nested LinearLayout) and if you are developing an application with a GUI a bit more complicated than a basic TextField and Button (specially when we have a repeating object like what we have in a ListView),Switching from LinearLayout to FrameLayout or RelativeLayout can tangibly improve the responsiveness of your application.
Just to see that using FrameLayout or RelativeLayout is pretty much as easy and as straight-forward as LinearLayout is, I'm gonna show you Six simple examples which will hopefully give you the idea of how they work so that you are gonna be able to choose a more efficient layout when you are designing a page.
Here are those Six examples I was talking about, I have used Buttons for all subviews just for the sake of simplicity but it can obviously be any other type of View:
and here is the XML source that generates each one of these layouts:
One of these basic principles in android is Layout management which is hugely being misused, I mean if you go through different android samples and blogs (including this Blog!) more than 70% of all samples and examples are using LinearLayout even for some very simple layout that could have been simply done by using a FrameLayout. I am no exception and actually I used to think if I am comfortable with LinearLayout and everybody else is using it, why should I use another layout? but the thing is LinearLayout is a pretty heavyweight LayoutManager compare to FrameLayout and a far less flexible layout compare to RelativeLayout (this inflexibility usually leads to one of those ugly layout files with 4 or 5 nested LinearLayout) and if you are developing an application with a GUI a bit more complicated than a basic TextField and Button (specially when we have a repeating object like what we have in a ListView),Switching from LinearLayout to FrameLayout or RelativeLayout can tangibly improve the responsiveness of your application.
Just to see that using FrameLayout or RelativeLayout is pretty much as easy and as straight-forward as LinearLayout is, I'm gonna show you Six simple examples which will hopefully give you the idea of how they work so that you are gonna be able to choose a more efficient layout when you are designing a page.
Here are those Six examples I was talking about, I have used Buttons for all subviews just for the sake of simplicity but it can obviously be any other type of View:
and here is the XML source that generates each one of these layouts:
1
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:paddingTop="30dip"
android:background="@color/black">
<Button
android:layout_width="fill_parent"
android:layout_height="90dip"
android:layout_alignParentTop="true"
android:text="View 1"
android:id="@+id/view1" />
<Button
android:layout_width="fill_parent"
android:layout_height="90dip"
android:layout_below="@id/view1"
android:text="View 2"
android:id="@+id/view2" />
<Button
android:layout_width="fill_parent"
android:layout_height="90dip"
android:layout_below="@id/view2"
android:text="View 3"
android:id="@+id/view3" />
<Button
android:layout_width="fill_parent"
android:layout_height="90dip"
android:layout_below="@id/view3"
android:text="View 4"
android:id="@+id/view4" />
<Button
android:layout_width="fill_parent"
android:layout_height="90dip"
android:layout_below="@id/view4"
android:text="View 5"
android:id="@+id/view5" />
</RelativeLayout> 2
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="5dip"
android:background="@color/black">
<Button
android:layout_width="100dip"
android:layout_height="fill_parent"
android:layout_gravity="left"
android:text="View 1"
android:id="@+id/view1" />
<Button
android:layout_width="100dip"
android:layout_height="fill_parent"
android:layout_gravity="center_horizontal"
android:text="View 2"
android:id="@+id/view2" />
<Button
android:layout_width="100dip"
android:layout_height="fill_parent"
android:layout_gravity="right"
android:text="View 3"
android:id="@+id/view3" />
</FrameLayout> 3
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<Button
android:layout_width="120dip"
android:layout_height="120dip"
android:layout_gravity="left|top"
android:layout_margin="15dip"
android:text="View 1"
android:id="@+id/view1" />
<Button
android:layout_width="120dip"
android:layout_height="120dip"
android:layout_gravity="right|top"
android:layout_margin="15dip"
android:text="View 2"
android:id="@+id/view2" />
<Button
android:layout_width="120dip"
android:layout_height="120dip"
android:layout_gravity="left|center_vertical"
android:layout_marginLeft="15dip"
android:text="View 3"
android:id="@+id/view3" />
<Button
android:layout_width="120dip"
android:layout_height="120dip"
android:layout_gravity="right|center_vertical"
android:layout_marginRight="15dip"
android:text="View 4"
android:id="@+id/view4" />
<Button
android:layout_width="120dip"
android:layout_height="120dip"
android:layout_gravity="left|bottom"
android:layout_margin="15dip"
android:text="View 5"
android:id="@+id/view5" />
<Button
android:layout_width="120dip"
android:layout_height="120dip"
android:layout_gravity="right|bottom"
android:layout_margin="15dip"
android:text="View 6"
android:id="@+id/view6" />
</FrameLayout> 4
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dip"
android:background="@color/black">
<Button
android:layout_width="150dip"
android:layout_height="120dip"
android:layout_gravity="left|top"
android:text="View 1"
android:id="@+id/view1" />
<Button
android:layout_width="150dip"
android:layout_height="120dip"
android:layout_gravity="right|top"
android:text="View 2"
android:id="@+id/view2" />
<Button
android:layout_width="fill_parent"
android:layout_height="200dip"
android:layout_gravity="center"
android:text="View 3"
android:id="@+id/view3" />
<Button
android:layout_width="150dip"
android:layout_height="120dip"
android:layout_gravity="left|bottom"
android:text="View 4"
android:id="@+id/view4" />
<Button
android:layout_width="150dip"
android:layout_height="120dip"
android:layout_gravity="right|bottom"
android:text="View 5"
android:id="@+id/view5" />
</FrameLayout> 5
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dip"
android:background="@color/black">
<Button
android:layout_width="120dip"
android:layout_height="200dip"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:text="View 1"
android:id="@+id/view1" />
<Button
android:layout_width="180dip"
android:layout_height="120dip"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:text="View 2"
android:id="@+id/view2" />
<Button
android:layout_width="120dip"
android:layout_height="100dip"
android:layout_alignParentLeft="true"
android:layout_below="@id/view1"
android:text="View 3"
android:id="@+id/view3" />
<Button
android:layout_width="180dip"
android:layout_height="180dip"
android:layout_alignParentRight="true"
android:layout_below="@id/view2"
android:text="View 4"
android:id="@+id/view4" />
<Button
android:layout_width="fill_parent"
android:layout_height="120dip"
android:layout_alignParentBottom="true"
android:layout_below="@id/view4"
android:text="View 5"
android:id="@+id/view5" />
</RelativeLayout> 6
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dip"
android:background="@color/black">
<Button
android:layout_width="180dip"
android:layout_height="180dip"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:text="View 1"
android:id="@+id/view1" />
<Button
android:layout_width="120dip"
android:layout_height="90dip"
android:layout_toLeftOf="@id/view1"
android:layout_alignParentLeft="true"
android:text="View 2"
android:id="@+id/view2" />
<Button
android:layout_width="120dip"
android:layout_height="90dip"
android:layout_toLeftOf="@id/view1"
android:layout_below="@id/view2"
android:layout_alignParentLeft="true"
android:text="View 3"
android:id="@+id/view3" />
<Button
android:layout_width="fill_parent"
android:layout_height="120dip"
android:layout_below="@id/view1"
android:text="View 4"
android:id="@+id/view4" />
<Button
android:layout_width="180dip"
android:layout_height="180dip"
android:layout_alignParentLeft="true"
android:layout_below="@id/view4"
android:text="View 5"
android:id="@+id/view5" />
<Button
android:layout_width="120dip"
android:layout_height="90dip"
android:layout_toRightOf="@id/view5"
android:layout_below="@id/view4"
android:layout_alignParentRight="true"
android:text="View 6"
android:id="@+id/view6" />
<Button
android:layout_width="120dip"
android:layout_height="90dip"
android:layout_toRightOf="@id/view5"
android:layout_below="@id/view6"
android:layout_alignParentRight="true"
android:text="View 7"
android:id="@+id/view7" />
</RelativeLayout> Friday, March 12, 2010
My First Android Application ;)
I was struggling with myself for a long time whether to do this or not, and finally I did it.
I uploaded my first Android application in Market yesterday and actually I feel excited about it...
It is not a full-feature application, just a simple Ebay-Search client which will let you search through Items in Ebay...no buying or bidding yet!!
since unfortunately I don't have any Android-enabled phone at the moment so I would really appreciate it if you could download this application, give it a go and let me know how it works on a real device.
Here's some screen-shots of my application :
I uploaded my first Android application in Market yesterday and actually I feel excited about it...
It is not a full-feature application, just a simple Ebay-Search client which will let you search through Items in Ebay...no buying or bidding yet!!
since unfortunately I don't have any Android-enabled phone at the moment so I would really appreciate it if you could download this application, give it a go and let me know how it works on a real device.
Here's some screen-shots of my application :
![]() | ![]() | ![]() |
![]() | ||
Monday, February 15, 2010
Android 2D ( A Simple Example )
It has been a long time I wanted to go through Android 2D capabilities and see how that works. and after a little bit of reading and researching about the topic, I developed a simple TicTacToe game which I'm gonna share it with you in this article.
The primary purpose of this example is to dig up some basic Android 2D features and get familiar with some game programming principles. Although TicTacToe is not really an interactive game, I think its simplicity will help us to be focused on what matters most which is actually how to do things in Android rather than how to design and develop an efficient game algorithm for a particular game. (I'm gonna use the same technique that has been used in LunarLandar application, which by the way is a pretty good example in this area).
first of all let's have a look at how our application will be looking like:
Despite the fact that TicTacToe is a pretty easy game to develop, Graphic wise I mean, and can be simply done by extending View Class, I have actually used 'SurfaceView', since apparently it's a better option for developing interactive games in Android. the most important point about SurfaceView class is that it uses two buffers, a drawing buffer and a displaying buffer, you are not allowed to draw directly on displaying buffer and if you need to draw something you will have to get the drawing buffer , fill it and post it as display buffer. as API documentation has mentioned there is no guarantee that anything gets preserved in drawing buffer and as a result we always need to draw anything we want to be shown without taking any presumption that something is already there from last drawing operation.
OK... now that we learned some basic facts about how SurfaceView works, let's have a look at my code and see what we have got there :
The first thing we're gonna need in any game is a Game Thread, The idea here is to constantly call a series of methods which are responsible to update some states and draw something based on those states. as I said before when you are dealing with SurfaceView you need to draw into the draw buffer and then post it on the surface, It can be done by calling lockCanvas() and unlockCanvasAndPost() methods of SurfaceHolder class, you can get the associated SurfaceHolder instance of your SurfaceView by calling getHolder() method. as you can see in the above code I have implemented the SurfaceHolder.Callback interface; this interface provides 3 callback methods for monitoring the life-cycle of the SurfaceView, here is my implementation of these methods:
in surfaceCreated() method I have initialized some variables and objects such as an instance of TicTacToe class (which will actually take care of game's logic and rules), background Image , cross and circle images and Paint objects that all will be used later to draw the game, here is also a good place to create and start our main thread. then we will kill the main thread in surfaceDestroyed() method to make sure no one's gonna make an attempt to draw anything on the surface after it has been destroyed.
now that we have got anything initialized and the main thread running let's see what is happening in each loop of our thread, as you saw in the first chunk of code above we are actually calling 2 methods each time, doDraw() and updateScores() :
The Last thing I would like to handle is the ability to keep the state of the game when user flips the phone and goes to landscape mode or vice versa, so we are gonna have to save current state of the game just before application get killed and then retrieve that state later when the activity get started again which mean our activity class will be something like this:
getInternalState() method returns the associated TicTacToe object of the TicTacToeView, as I said before TicTacToe class is just a simple class that holds the game matrix and players' scores and actually that's all we need to save and retrieve each time the configuration gets changed in this example.
I also though it would be interesting to start the game with some sort of animation so I wrote a piece of code to have the game table being drawn when you start the application, something like this I mean :
and here is the code that generates this animation:
I'm not sure if it's the best way to do this but it works just like i want it to work ;). (since just calling lockCanvas() and unlockCanvasAndPost() alone takes something around 200ms on my emulator - which is of course ridiculous but i have no idea why! - I didn't need to add any delay but on real device it will probably be needed to have some delay).
The primary purpose of this example is to dig up some basic Android 2D features and get familiar with some game programming principles. Although TicTacToe is not really an interactive game, I think its simplicity will help us to be focused on what matters most which is actually how to do things in Android rather than how to design and develop an efficient game algorithm for a particular game. (I'm gonna use the same technique that has been used in LunarLandar application, which by the way is a pretty good example in this area).
first of all let's have a look at how our application will be looking like:
![]() | ![]() |
Despite the fact that TicTacToe is a pretty easy game to develop, Graphic wise I mean, and can be simply done by extending View Class, I have actually used 'SurfaceView', since apparently it's a better option for developing interactive games in Android. the most important point about SurfaceView class is that it uses two buffers, a drawing buffer and a displaying buffer, you are not allowed to draw directly on displaying buffer and if you need to draw something you will have to get the drawing buffer , fill it and post it as display buffer. as API documentation has mentioned there is no guarantee that anything gets preserved in drawing buffer and as a result we always need to draw anything we want to be shown without taking any presumption that something is already there from last drawing operation.
OK... now that we learned some basic facts about how SurfaceView works, let's have a look at my code and see what we have got there :
public class TicTacToeView extends SurfaceView implements SurfaceHolder.Callback, OnTouchListener {
.
.
.
public TicTacToeView(Context context) {
super(context);
getHolder().addCallback(this);
}
public TicTacToeView(Context context,TicTacToe internalState) {
this(context);
this.tictactoe = internalState;
}
.
.
.
class MainThread extends Thread {
private SurfaceHolder surfaceHolder;
private boolean runFlag = false;
boolean firstTime = true;
public MainThread(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
}
public void setRunning(boolean run) {
this.runFlag = run;
}
@Override
public void run() {
Canvas c;
while (this.runFlag) {
if(firstTime){
drawLines();
firstTime = false;
continue;
}
c = null;
try {
c = this.surfaceHolder.lockCanvas(null);
synchronized (this.surfaceHolder) {
doDraw(c);
updateScores(c);
}
} finally {
if (c != null) {
this.surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
.
.
.
}
.
.
.
}
The first thing we're gonna need in any game is a Game Thread, The idea here is to constantly call a series of methods which are responsible to update some states and draw something based on those states. as I said before when you are dealing with SurfaceView you need to draw into the draw buffer and then post it on the surface, It can be done by calling lockCanvas() and unlockCanvasAndPost() methods of SurfaceHolder class, you can get the associated SurfaceHolder instance of your SurfaceView by calling getHolder() method. as you can see in the above code I have implemented the SurfaceHolder.Callback interface; this interface provides 3 callback methods for monitoring the life-cycle of the SurfaceView, here is my implementation of these methods:
.
.
.
@Override
public void surfaceCreated(SurfaceHolder holder) {
_thread = new MainThread(getHolder());
if(tictactoe == null)
this.tictactoe = new TicTacToe(new TicTacToeHandler(),false);
else
_thread.firstTime = false;
Resources resources = getContext().getResources();
this.background = BitmapFactory.decodeResource(resources, R.drawable.background);
this.X_Player = BitmapFactory.decodeResource(resources, R.drawable.x_pic);
this.O_Player = BitmapFactory.decodeResource(resources, R.drawable.o_pic);
Rect rect = holder.getSurfaceFrame();
this.gameTable_height = rect.height()-56;
this.gameTable_sY = 0;
this.gameTable_width = rect.width();
this.scoreBox_Height = 50;
this.scoreBox_Width = rect.width();
this.scoreBox_sY = rect.height()-this.scoreBox_Height;
this.squares = new Rect[9];
final int square_Width = (int)(gameTable_width-(TABLE_BORDER*4))/3;
final int square_Height = (int)(gameTable_height-(TABLE_BORDER*4))/3;
for(int i=0;i<9;i++){
int row = i%3;
int column = i/3;
int left = ((row+1)*TABLE_BORDER)+(square_Width*row);
int top = ((column+1)*TABLE_BORDER)+(square_Height*column);
this.squares[i] = new Rect(left,top,left+square_Width,top+square_Height);
}
this.tablePaint.setStyle(Style.STROKE);
this.tablePaint.setShader(new LinearGradient(0, 0, this.gameTable_height,
this.gameTable_width,
0xFF000000,
0xFF343434,
TileMode.MIRROR));
this.tablePaint.setStrokeWidth(TABLE_BORDER);
this.tablePaint.setAlpha(0xCC);
this.boxPaint.setStyle(Style.STROKE);
this.boxPaint.setStrokeWidth(BOX_BORDER);
this.boxPaint.setColor(0xFFA90000);
this.textPaint.setColor(0xFF000000);
this.textPaint.setTextAlign(Align.CENTER);
this.textPaint.setTextSize(14);
this.textPaint.setTypeface(Typeface.createFromAsset(getContext().getResources().getAssets(),"HARLOWSI.TTF"));
this.contentPaint.setAlpha(0xDF);
_thread.setRunning(true);
_thread.start();
setOnTouchListener(this);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
}
.
.
.
in surfaceCreated() method I have initialized some variables and objects such as an instance of TicTacToe class (which will actually take care of game's logic and rules), background Image , cross and circle images and Paint objects that all will be used later to draw the game, here is also a good place to create and start our main thread. then we will kill the main thread in surfaceDestroyed() method to make sure no one's gonna make an attempt to draw anything on the surface after it has been destroyed.
now that we have got anything initialized and the main thread running let's see what is happening in each loop of our thread, as you saw in the first chunk of code above we are actually calling 2 methods each time, doDraw() and updateScores() :
.
.
.
public void doDraw(Canvas canvas) {
canvas.drawBitmap(this.background, 0, 0, null);
drawTable(canvas);
if(this.draw && Xs[0] == Xs[1] && Ys[0] == Ys[1]){
CheckContent();
}
drawContent(canvas);
}
/////////
private void updateScores(Canvas canvas){
final int halfBorder = (int)BOX_BORDER/2;
final Paint paint = textPaint;
Rect rect = new Rect(halfBorder,
this.scoreBox_sY-halfBorder,
this.scoreBox_Width-halfBorder,
(this.scoreBox_sY-halfBorder)+this.scoreBox_Height);
RectF rectf = new RectF(rect);
canvas.drawRoundRect(rectf, 15, 15, this.boxPaint);
canvas.drawText("Your Score : "+this.tictactoe.getYourScore(), 65,this.scoreBox_sY+25,paint);
canvas.drawText("Computer's Score : "+this.tictactoe.getOpponentScore(), 15+(this.scoreBox_Width/3)*2,this.scoreBox_sY+25,paint);
}
private void drawTable(Canvas canvas){
final Paint paint = this.tablePaint;
final float border = TABLE_BORDER;
final int cellHeight = (int)(this.gameTable_height-(2*border))/3;
final int cellWidth = (int)(this.gameTable_width-(2*border))/3;
final float table_eX = this.gameTable_width-border;
final float table_eY = this.gameTable_height-border;
canvas.drawLine(this.gameTable_sY+border, cellHeight+border, table_eX, cellHeight+border, paint);
canvas.drawLine(this.gameTable_sY+border, (cellHeight*2)+border, table_eX, (cellHeight*2)+border, paint);
canvas.drawLine(cellWidth+border, border, cellWidth+border, table_eY, paint);
canvas.drawLine((cellWidth*2)+border, border, (cellWidth*2)+border, table_eY, paint);
paint.setPathEffect(new CornerPathEffect(20));
canvas.drawRect(border/2, border/2, this.gameTable_width-(border/2),
this.gameTable_height-(border/2),
paint);
paint.setPathEffect(null);
}
private void CheckContent(){
for(int i=0;i<9;i++){
if(this.squares[i].contains(Xs[1], Ys[1])){
this.tictactoe.move_Request(i);
this.draw = false;
return;
}
}
}
private void drawContent(Canvas canvas){
for(int i=0;i<9;i++){
int squareContent = this.tictactoe.getContent(i);
if(squareContent == TicTacToe.X_PLAYER)
canvas.drawBitmap(this.X_Player,null,this.squares[i], this.contentPaint);
else if(squareContent == TicTacToe.O_PLAYER)
canvas.drawBitmap(this.O_Player,null,this.squares[i], this.contentPaint);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
this.Xs[0] = (int)event.getX();
this.Ys[0] = (int)event.getY();
}
else if(event.getAction() == MotionEvent.ACTION_UP){
this.Xs[1] = (int)event.getX();
this.Ys[1] = (int)event.getY();
this.draw = true;
}
return true;
}
.
.
.
The Last thing I would like to handle is the ability to keep the state of the game when user flips the phone and goes to landscape mode or vice versa, so we are gonna have to save current state of the game just before application get killed and then retrieve that state later when the activity get started again which mean our activity class will be something like this:
public class TicTacToeActivity extends Activity{
private TicTacToeView view;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
TicTacToe oldTTT = (TicTacToe)getLastNonConfigurationInstance();
if(oldTTT != null)
this.view = new TicTacToeView(this, oldTTT);
else
this.view = new TicTacToeView(this);
setContentView(this.view);
}
@Override
public Object onRetainNonConfigurationInstance(){
return this.view.getInternalState();
}
}
getInternalState() method returns the associated TicTacToe object of the TicTacToeView, as I said before TicTacToe class is just a simple class that holds the game matrix and players' scores and actually that's all we need to save and retrieve each time the configuration gets changed in this example.
I also though it would be interesting to start the game with some sort of animation so I wrote a piece of code to have the game table being drawn when you start the application, something like this I mean :
![]() | ![]() |
and here is the code that generates this animation:
.
.
.
public void run(){
final int length = lines.length;
for(int i=0;i<length;i++)
drawLine(i);
}
private void drawLine(int index){
final Line line = this.lines[index];
final float addingPortion_X = ((line.eX - line.sX) / DRAW_PER_LINE);
final float addingPortion_Y = ((line.eY - line.sY) / DRAW_PER_LINE);
for(int i=0;i<DRAW_PER_LINE;i++){
Canvas canvas = this.holder.lockCanvas(null);
canvas.drawBitmap(background, 0, 0, null);
for(int j=0;j<index;j++)
canvas.drawLine(this.lines[j].sX,
this.lines[j].sY,
this.lines[j].eX,
this.lines[j].eY,
this.paint);
canvas.drawLine(line.sX,
line.sY,
line.sX+(addingPortion_X*(i+1)),
line.sY+(addingPortion_Y*(i+1)),
this.paint);
this.holder.unlockCanvasAndPost(canvas);
}
.
.
.
I'm not sure if it's the best way to do this but it works just like i want it to work ;). (since just calling lockCanvas() and unlockCanvasAndPost() alone takes something around 200ms on my emulator - which is of course ridiculous but i have no idea why! - I didn't need to add any delay but on real device it will probably be needed to have some delay).
Saturday, January 30, 2010
Android WebView
Android WebView enables us to display web pages and interact with web content, there are several reasons that you might want to use WebView in your applications, but i think one the most interesting features of WebView is JavaScript integration capability. in this article I'm gonna show you a simple example of how easy it is to call javascript functions from your java code and vice versa.
first of all let's see how our example will be looking like :

it's actually just a WebView which is displaying a local Html page, this html page contains a java script slideshow component, I've actually used the very same code that i found over here. the original javascript component has two button which can be used to slide into the next or previous page, but what I wanted was to be able to slide through pages using Swiping Gestures, Like...Swipe Left...go to next page, Swipe Right ...previous page.

There are two javaScript functions , moveBackward() which replaces the current Item with previous one using a left to right sliding effect and moveForward() which does the exact opposite thing. all we need to do is to detect user Gestures, if it is a right to left gesture (swipe left) we will call moveForward()function and if it is a swipe right gesture we will call moveBackward() function, in this example I've used SimpleGestureFilter class (see my last post) and here is our onSwipe method implementation:
It's much easier than you though it would be, isn't it? we just need to pass the name of our javascript function (with 'javascript:' prefix) to loadUrl() method of the WebView instance.
as you can see in the pictures above, we have 3 buttons in our html form 'Add', 'Remove' and 'Report', we also have a Vector of Strings in our Activity; when 'Add' button is pressed the name of the current item will be sent to our activity and got stored in the Vector, when 'Remove' button is pressed the name of current item will be removed from Vector and in both cases a message will be shown afterwords that the operation has just been done :

if user presses the "Report" button a javascript popup box will be shown which says how many items are currently stored in our Vector:

here is the html and javascript code that is being used to render those 3 buttons:
and here is our Activity source code :
as you can see all we have is a simple WebView instance which displays the content of 'test.html' file that is located in assets directory of our project.
we've called addJavascriptInterface() method of our WebView instance in order to be able to call java methods in javascript code this method has two parameters, the first one is an Object methods of which we want to call in our javascript code and the second parameter is an String which will be used as the name of that java object in javascript context. once addJavascriptInterface() method is called the specified java object will be attached to the Html document's window object and can be referred by its name.
Remember that when methods of the passed java object is called in javascript context, it's not gonna invoked in the Main(UI) thread, and if you need to get it called in the UI thread you will need to use a Handler and post a new runnable to run your code, just like what I've done for add() and remove() methods in this example (though it is not necessary in this example).
another important thing to remember is the fact that you cannot pass or return any complex object to or from javascript context, that is all you are allowed to use are primitive and String data types.
first of all let's see how our example will be looking like :

it's actually just a WebView which is displaying a local Html page, this html page contains a java script slideshow component, I've actually used the very same code that i found over here. the original javascript component has two button which can be used to slide into the next or previous page, but what I wanted was to be able to slide through pages using Swiping Gestures, Like...Swipe Left...go to next page, Swipe Right ...previous page.

There are two javaScript functions , moveBackward() which replaces the current Item with previous one using a left to right sliding effect and moveForward() which does the exact opposite thing. all we need to do is to detect user Gestures, if it is a right to left gesture (swipe left) we will call moveForward()function and if it is a swipe right gesture we will call moveBackward() function, in this example I've used SimpleGestureFilter class (see my last post) and here is our onSwipe method implementation:
@Override
public void onSwipe(int direction) {
switch (direction) {
case SimpleGestureFilter.SWIPE_RIGHT : webView.loadUrl("javascript:moveBackward()");
break;
case SimpleGestureFilter.SWIPE_LEFT : webView.loadUrl("javascript:moveForward()");
break;
case SimpleGestureFilter.SWIPE_DOWN :
case SimpleGestureFilter.SWIPE_UP :
}
}
It's much easier than you though it would be, isn't it? we just need to pass the name of our javascript function (with 'javascript:' prefix) to loadUrl() method of the WebView instance.
as you can see in the pictures above, we have 3 buttons in our html form 'Add', 'Remove' and 'Report', we also have a Vector of Strings in our Activity; when 'Add' button is pressed the name of the current item will be sent to our activity and got stored in the Vector, when 'Remove' button is pressed the name of current item will be removed from Vector and in both cases a message will be shown afterwords that the operation has just been done :

if user presses the "Report" button a javascript popup box will be shown which says how many items are currently stored in our Vector:

here is the html and javascript code that is being used to render those 3 buttons:
.
.
.
var items = ["British Columbia", "Ontario", "Yukon","New Brunswick"];
function report(){
var contentStr = window.interface.getReportContent();
var contentDiv = document.getElementById("showBoxContent");
contentDiv.innerHTML = contentStr;
showBox();
}
.
.
.
<input type="button" onclick="window.interface.add(items[configParams.selected])" value="Add"/>
<input type="button" onclick="window.interface.remove(items[configParams.selected])" value="Remove"/>
<input type="button" onclick="report()" value="Report"/>
.
.
.
and here is our Activity source code :
public class WebViewSample extends Activity implements SimpleGestureListener{
private Handler handler = new Handler();
private WebView webView;
private SimpleGestureFilter filter;
private Vector<String> provinces = new Vector();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.webview_layout);
webView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
webSettings.setSaveFormData(false);
webSettings.setJavaScriptEnabled(true);
this.filter = new SimpleGestureFilter(this,this);
this.filter.setMode(SimpleGestureFilter.MODE_TRANSPARENT);
webView.addJavascriptInterface(new MyJavaScriptInterface(), "interface");
webView.loadUrl("file:///android_asset/test.html");
}
@Override
public boolean dispatchTouchEvent(MotionEvent me){
this.filter.onTouchEvent(me);
return super.dispatchTouchEvent(me);
}
@Override
public void onSwipe(int direction) {
switch (direction) {
case SimpleGestureFilter.SWIPE_RIGHT : webView.loadUrl("javascript:moveBackward()");
break;
case SimpleGestureFilter.SWIPE_LEFT : webView.loadUrl("javascript:moveForward()");
break;
case SimpleGestureFilter.SWIPE_DOWN :
case SimpleGestureFilter.SWIPE_UP :
}
}
@Override
public void onDoubleTap() {
}
private void addOrRemove(String name,boolean add){
String msg = null;
boolean alreadyAdded = this.provinces.contains(name);
if(add){
if(!alreadyAdded){
this.provinces.add(name);
msg = name+" Has just been added to the List.";
}
else
msg = name+" Has already been added to the list.";
}
else{
if(alreadyAdded){
this.provinces.remove(name);
msg = name+" Has just been removed from the List.";
}
else
msg = name+" has never been added to the list!";
}
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
final class MyJavaScriptInterface {
MyJavaScriptInterface() {
}
public void add(final String name) {
handler.post(new Runnable() {
public void run() {
addOrRemove(name, true);
}
});
}
public void remove(final String name) {
handler.post(new Runnable() {
public void run() {
addOrRemove(name, false);
}
});
}
public String getReportContent() {
if(provinces.size() == 0)
return "<i> The list is empty. </i>";
String content = "<i>You have added these provinces into the list :<i><br/><br/>";
for(int i=0;i<provinces.size();i++)
content += (i+1)+"- "+provinces.get(i)+"<br/>";
return content;
}
}
}
as you can see all we have is a simple WebView instance which displays the content of 'test.html' file that is located in assets directory of our project.
we've called addJavascriptInterface() method of our WebView instance in order to be able to call java methods in javascript code this method has two parameters, the first one is an Object methods of which we want to call in our javascript code and the second parameter is an String which will be used as the name of that java object in javascript context. once addJavascriptInterface() method is called the specified java object will be attached to the Html document's window object and can be referred by its name.
Remember that when methods of the passed java object is called in javascript context, it's not gonna invoked in the Main(UI) thread, and if you need to get it called in the UI thread you will need to use a Handler and post a new runnable to run your code, just like what I've done for add() and remove() methods in this example (though it is not necessary in this example).
another important thing to remember is the fact that you cannot pass or return any complex object to or from javascript context, that is all you are allowed to use are primitive and String data types.
Tuesday, January 19, 2010
Android Gestures
Like it or not touch screens are becoming part of both developers and users life, i mean dont think i would buy one of Those uncool phones without touchscreen unless i really have to, would you? but the important point is that what is the Touchscreen use if applications doesn't support touchscreen interaction, in other words who is really gonna pay for a ,say, Picture management application if it does not support some gestures for switching between pictures or zooming? [ Gesture Detection in Android ].
In Android there are three levels of touch screen event handling mechanism which can be used by developers. the most low level technique is to receive all touch events and take care of all things, you can attach an 'OnTouchListener' to a view and get notified whenever there is a touch event or you can override onTouchEvent() or dispatchTouchEvent() method of your activity or view, in all these cases you would be dealing with an instance of MotionEvent every single time and you would have to detect what user is doing all on your own which will suit requirments for developing games and stuff like that. But it is just too much hassle if you only need a few simple gestures for your application.
Next approach is to use GestureDetector class along with OnGestureListener and/or OnDoubleTapListener, in this technique whenever there is a new MotionEvent you have to pass it to Gesture Detector's onTouchEvent() method, it then will analyse this event and previous events and tell you what is happening on the screen by calling some of the callback methods.
here is a simple activity which uses GestureDetector :
if we want to understand all gesture types and how they work, first of all we need to know three basic MotionEvents which can combine with each other and create some gestures, these three Events are Action_Down , Action_Move and Action_Up , each time you touch the screen an Action_Down occurs and when you start moving it will create Action_Move event and finally when you take your finger off the screen an Action_Up Event will be created.
onDown() is called simply when there is an Action_Down event.
onShowPress() is called after onDown() and before any other callback method, I found out it sometimes might not get called for example when you tap on the screen so fast, but it's actually what this method all about, to make a distinction between a possible unintentional touch and an intentional one.
onSingleTapUp() is called when there is a Tap Gesture. Tap Gesture happens when an Action_Down event followed by an Action_Up event(like a Single-Click).
onDoubleTap() is called when there is two consecutive Tap gesture(like a Double-Click).
onSingleTapConfirmed() is so similar to onSingleTapUp() but it is only get called when the detected tap gesture is definitely a single Tap and not part of a double tap gesture.
Here is the sequence in which these callback methods are called when we tap on the screen:
onDown() – onShowPress() - onSingleTapUp() – onSingleTapConfirmed()
and here is when we do a double tap:
onDown() – onShowPress() - onSingleTapUp() – onDoubleTap() – onDoubleTapEvent()
onDown() – onShowPress() – onDoubleTapEvent()
onFling() is called when a Fling Gesture is detected. fling Gesture occurs when there is an Action_Down then some Action_Move events and finally an Action_Up, but they must take place with a specified movement and velocity pattern to be considered as a fling gesture. for example if you put your finger on the screen and start moving it slowly and then remove your finger gently it won’t be count as a fling gesture.
onScroll() is usually called when there is a Action_Move event so if you put your finger on the screen and move it for a few seconds there will be a method call chain like this :
onDown() – onShowPress() – onScroll() - onScroll() - onScroll() - ....... - onScroll()
or If the movement was a Fling Gesture, then there would be a call chain like this :
onDown() – onShowPress() – onScroll() - onScroll() - onScroll() - ....... – onFling()
If there is an Action_Move event between first tap and second tap of a doubleTap gesture it will be handled by calling onDoubleTapEvent() instead of onScroll() method. onDoubleTapEvent() receives all Action_Up events of a doubleTap gesture as well.
Remember that if we touch the screen and don’t remove our finger for a specified amount of time onLongPress() method is called and in most cases there will be no further event callback regardless of whatever we do after that, moving or removing our finger. we can easily change this behavior of detector by calling setIsLongpressEnabled() method of GestureDetector class.
although GestureDetector makes our life much easier, it still could be a real pain in the ass if we would need to handle some complicated gestures, imagine you need an application which should do task1 when there is a circle gesture, task2 for rectangle gesture and task3 for triangle gesture. obviously it would not be so pleasant to deal with such a situation with those mechanism we have seen so far, it's actually when Gesture API and Gesture Builder Application come into play.
Gesture Builder Application comes with Android emulator as a pre-installed app, It helps us to simply make new gestures and save them on the device, then we can retrieve and detect those gestures later using Gesture API. I'm not gonna talk about Gesture Builder app here,since there is a pretty good Article about it on Android Developers blog.
the only thing I'd like to mention here is GestureOverlayView class, It is actually just a transparent FrameLayout which can detect gestures but the thing is it can be used in two completely different ways, you can either put other views inside it and use it as a parent view or put it is the last child view of a FrameLayout (or any other way which causes it to be placed on top of another view).
In the first Scenario all child views will receive Touch events, therefore we will be able to press buttons or interact with other widgets as well as doing some gestures, on the other hand if GestureOverlayView has been placed on top, it will swallow all Touch events and no underlay view will be notified for any touch Event.
Although Gesture API brings many useful features for us, I personally prefer to use GestureDetector for some simple, basic gestures and honestly I feel like something is missing here, I mean , apart from games, I would say more than 70% of all gestures that might be needed in our applications are just a simple sliding in different directions or a double tap. and that's why I have decided to come up with something easy which can enable us to handle those 70% as simple as possible. how simple? you might be asking...
here is how our previous activity will look like if we use SimpleGestureFilter class :
and here is SimpleGestureFilter source code :
as you can see clients of these class can determine the minimum and maximum distance and also minimum velocity which is required for a movement on screen to be considered as a Swipe Gesture, I also thought it would be great if our filter can behave differently like what GestureOverlayView can do and even more than that!
this Filter can run in three different mode: Transparent, Solid and Dynamic. in Transparent mode it will work just like when we have a GestureOverlayView as parent: all views will receive Touch events; Solid mode works like when we put a GestureOverlayView as a child view: no one will receive TouchEvent, it is not as efficient as GestureOverlayView is, since we actually let all events get passed but what we do is we literally kill them when they are passing through our filter ;).
the last mode is Dynamic mode, the primary purpose of this mode is to have a bit smarter gesture detection, i mean there has been sometimes that i wanted to slide from one page to another, but a button get pressed and something else happens. it does not happen so much but it is really annoying. what i tried to do in Dynamic mode is to distinguish between a swipe/double tap gesture and a movement which is neither of them. so if you have a view full of buttons and other interactive stuff and user does a swipe or double tap gesture, it is guaranteed (although i believe there is no such thing as guarantee in life ;) ) that no other event callback will be called but only onSwipe() or onDoubleTap().
Anyway that's what i came up with to take the pain away in those circumstances when we just need to handle some simple Gestures.
hope it will be helpful for you and can make your life a bit easier.
In Android there are three levels of touch screen event handling mechanism which can be used by developers. the most low level technique is to receive all touch events and take care of all things, you can attach an 'OnTouchListener' to a view and get notified whenever there is a touch event or you can override onTouchEvent() or dispatchTouchEvent() method of your activity or view, in all these cases you would be dealing with an instance of MotionEvent every single time and you would have to detect what user is doing all on your own which will suit requirments for developing games and stuff like that. But it is just too much hassle if you only need a few simple gestures for your application.
Next approach is to use GestureDetector class along with OnGestureListener and/or OnDoubleTapListener, in this technique whenever there is a new MotionEvent you have to pass it to Gesture Detector's onTouchEvent() method, it then will analyse this event and previous events and tell you what is happening on the screen by calling some of the callback methods.
here is a simple activity which uses GestureDetector :
public class SimpleActivity extends Activity implements OnGestureListener,
OnDoubleTapListener{
private GestureDetector detector;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
detector = new GestureDetector(this,this);
}
@Override
public boolean onTouchEvent(MotionEvent me){
this.detector.onTouchEvent(me);
return super.onTouchEvent(me);
}
@Override
public boolean onDown(MotionEvent e) {
Log.d("---onDown----",e.toString());
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
Log.d("---onFling---",e1.toString()+e2.toString());
return false;
}
@Override
public void onLongPress(MotionEvent e) {
Log.d("---onLongPress---",e.toString());
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
Log.d("---onScroll---",e1.toString()+e2.toString());
return false;
}
@Override
public void onShowPress(MotionEvent e) {
Log.d("---onShowPress---",e.toString());
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.d("---onSingleTapUp---",e.toString());
return false;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Log.d("---onDoubleTap---",e.toString());
return false;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
Log.d("---onDoubleTapEvent---",e.toString());
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.d("---onSingleTapConfirmed---",e.toString());
return false;
}
}
if we want to understand all gesture types and how they work, first of all we need to know three basic MotionEvents which can combine with each other and create some gestures, these three Events are Action_Down , Action_Move and Action_Up , each time you touch the screen an Action_Down occurs and when you start moving it will create Action_Move event and finally when you take your finger off the screen an Action_Up Event will be created.
onDown() is called simply when there is an Action_Down event.
onShowPress() is called after onDown() and before any other callback method, I found out it sometimes might not get called for example when you tap on the screen so fast, but it's actually what this method all about, to make a distinction between a possible unintentional touch and an intentional one.
onSingleTapUp() is called when there is a Tap Gesture. Tap Gesture happens when an Action_Down event followed by an Action_Up event(like a Single-Click).
onDoubleTap() is called when there is two consecutive Tap gesture(like a Double-Click).
onSingleTapConfirmed() is so similar to onSingleTapUp() but it is only get called when the detected tap gesture is definitely a single Tap and not part of a double tap gesture.
Here is the sequence in which these callback methods are called when we tap on the screen:
onDown() – onShowPress() - onSingleTapUp() – onSingleTapConfirmed()
and here is when we do a double tap:
onDown() – onShowPress() - onSingleTapUp() – onDoubleTap() – onDoubleTapEvent()
onDown() – onShowPress() – onDoubleTapEvent()
onFling() is called when a Fling Gesture is detected. fling Gesture occurs when there is an Action_Down then some Action_Move events and finally an Action_Up, but they must take place with a specified movement and velocity pattern to be considered as a fling gesture. for example if you put your finger on the screen and start moving it slowly and then remove your finger gently it won’t be count as a fling gesture.
onScroll() is usually called when there is a Action_Move event so if you put your finger on the screen and move it for a few seconds there will be a method call chain like this :
onDown() – onShowPress() – onScroll() - onScroll() - onScroll() - ....... - onScroll()
or If the movement was a Fling Gesture, then there would be a call chain like this :
onDown() – onShowPress() – onScroll() - onScroll() - onScroll() - ....... – onFling()
If there is an Action_Move event between first tap and second tap of a doubleTap gesture it will be handled by calling onDoubleTapEvent() instead of onScroll() method. onDoubleTapEvent() receives all Action_Up events of a doubleTap gesture as well.
Remember that if we touch the screen and don’t remove our finger for a specified amount of time onLongPress() method is called and in most cases there will be no further event callback regardless of whatever we do after that, moving or removing our finger. we can easily change this behavior of detector by calling setIsLongpressEnabled() method of GestureDetector class.
although GestureDetector makes our life much easier, it still could be a real pain in the ass if we would need to handle some complicated gestures, imagine you need an application which should do task1 when there is a circle gesture, task2 for rectangle gesture and task3 for triangle gesture. obviously it would not be so pleasant to deal with such a situation with those mechanism we have seen so far, it's actually when Gesture API and Gesture Builder Application come into play.
Gesture Builder Application comes with Android emulator as a pre-installed app, It helps us to simply make new gestures and save them on the device, then we can retrieve and detect those gestures later using Gesture API. I'm not gonna talk about Gesture Builder app here,since there is a pretty good Article about it on Android Developers blog.
the only thing I'd like to mention here is GestureOverlayView class, It is actually just a transparent FrameLayout which can detect gestures but the thing is it can be used in two completely different ways, you can either put other views inside it and use it as a parent view or put it is the last child view of a FrameLayout (or any other way which causes it to be placed on top of another view).
In the first Scenario all child views will receive Touch events, therefore we will be able to press buttons or interact with other widgets as well as doing some gestures, on the other hand if GestureOverlayView has been placed on top, it will swallow all Touch events and no underlay view will be notified for any touch Event.
Although Gesture API brings many useful features for us, I personally prefer to use GestureDetector for some simple, basic gestures and honestly I feel like something is missing here, I mean , apart from games, I would say more than 70% of all gestures that might be needed in our applications are just a simple sliding in different directions or a double tap. and that's why I have decided to come up with something easy which can enable us to handle those 70% as simple as possible. how simple? you might be asking...
here is how our previous activity will look like if we use SimpleGestureFilter class :
public class SimpleActivity extends Activity implements SimpleGestureListener{
private SimpleGestureFilter detector;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
detector = new SimpleGestureFilter(this,this);
}
@Override
public boolean dispatchTouchEvent(MotionEvent me){
this.detector.onTouchEvent(me);
return super.dispatchTouchEvent(me);
}
@Override
public void onSwipe(int direction) {
String str = "";
switch (direction) {
case SimpleGestureFilter.SWIPE_RIGHT : str = "Swipe Right";
break;
case SimpleGestureFilter.SWIPE_LEFT : str = "Swipe Left";
break;
case SimpleGestureFilter.SWIPE_DOWN : str = "Swipe Down";
break;
case SimpleGestureFilter.SWIPE_UP : str = "Swipe Up";
break;
}
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
@Override
public void onDoubleTap() {
Toast.makeText(this, "Double Tap", Toast.LENGTH_SHORT).show();
}
}
and here is SimpleGestureFilter source code :
public class SimpleGestureFilter extends SimpleOnGestureListener{
public final static int SWIPE_UP = 1;
public final static int SWIPE_DOWN = 2;
public final static int SWIPE_LEFT = 3;
public final static int SWIPE_RIGHT = 4;
public final static int MODE_TRANSPARENT = 0;
public final static int MODE_SOLID = 1;
public final static int MODE_DYNAMIC = 2;
private final static int ACTION_FAKE = -13; //just an unlikely number
private int swipe_Min_Distance = 100;
private int swipe_Max_Distance = 350;
private int swipe_Min_Velocity = 100;
private int mode = MODE_DYNAMIC;
private boolean running = true;
private boolean tapIndicator = false;
private Activity context;
private GestureDetector detector;
private SimpleGestureListener listener;
public SimpleGestureFilter(Activity context,SimpleGestureListener sgl) {
this.context = context;
this.detector = new GestureDetector(context, this);
this.listener = sgl;
}
public void onTouchEvent(MotionEvent event){
if(!this.running)
return;
boolean result = this.detector.onTouchEvent(event);
if(this.mode == MODE_SOLID)
event.setAction(MotionEvent.ACTION_CANCEL);
else if (this.mode == MODE_DYNAMIC) {
if(event.getAction() == ACTION_FAKE)
event.setAction(MotionEvent.ACTION_UP);
else if (result)
event.setAction(MotionEvent.ACTION_CANCEL);
else if(this.tapIndicator){
event.setAction(MotionEvent.ACTION_DOWN);
this.tapIndicator = false;
}
}
//else just do nothing, it's Transparent
}
public void setMode(int m){
this.mode = m;
}
public int getMode(){
return this.mode;
}
public void setEnabled(boolean status){
this.running = status;
}
public void setSwipeMaxDistance(int distance){
this.swipe_Max_Distance = distance;
}
public void setSwipeMinDistance(int distance){
this.swipe_Min_Distance = distance;
}
public void setSwipeMinVelocity(int distance){
this.swipe_Min_Velocity = distance;
}
public int getSwipeMaxDistance(){
return this.swipe_Max_Distance;
}
public int getSwipeMinDistance(){
return this.swipe_Min_Distance;
}
public int getSwipeMinVelocity(){
return this.swipe_Min_Velocity;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
final float xDistance = Math.abs(e1.getX() - e2.getX());
final float yDistance = Math.abs(e1.getY() - e2.getY());
if(xDistance > this.swipe_Max_Distance || yDistance > this.swipe_Max_Distance)
return false;
velocityX = Math.abs(velocityX);
velocityY = Math.abs(velocityY);
boolean result = false;
if(velocityX > this.swipe_Min_Velocity && xDistance > this.swipe_Min_Distance){
if(e1.getX() > e2.getX()) // right to left
this.listener.onSwipe(SWIPE_LEFT);
else
this.listener.onSwipe(SWIPE_RIGHT);
result = true;
}
else if(velocityY > this.swipe_Min_Velocity && yDistance > this.swipe_Min_Distance){
if(e1.getY() > e2.getY()) // bottom to up
this.listener.onSwipe(SWIPE_UP);
else
this.listener.onSwipe(SWIPE_DOWN);
result = true;
}
return result;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
this.tapIndicator = true;
return false;
}
@Override
public boolean onDoubleTap(MotionEvent arg0) {
this.listener.onDoubleTap();;
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent arg0) {
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent arg0) {
if(this.mode == MODE_DYNAMIC){ // we owe an ACTION_UP, so we fake an
arg0.setAction(ACTION_FAKE); //action which will be converted to an ACTION_UP later.
this.context.dispatchTouchEvent(arg0);
}
return false;
}
static interface SimpleGestureListener{
void onSwipe(int direction);
void onDoubleTap();
}
}
as you can see clients of these class can determine the minimum and maximum distance and also minimum velocity which is required for a movement on screen to be considered as a Swipe Gesture, I also thought it would be great if our filter can behave differently like what GestureOverlayView can do and even more than that!
this Filter can run in three different mode: Transparent, Solid and Dynamic. in Transparent mode it will work just like when we have a GestureOverlayView as parent: all views will receive Touch events; Solid mode works like when we put a GestureOverlayView as a child view: no one will receive TouchEvent, it is not as efficient as GestureOverlayView is, since we actually let all events get passed but what we do is we literally kill them when they are passing through our filter ;).
the last mode is Dynamic mode, the primary purpose of this mode is to have a bit smarter gesture detection, i mean there has been sometimes that i wanted to slide from one page to another, but a button get pressed and something else happens. it does not happen so much but it is really annoying. what i tried to do in Dynamic mode is to distinguish between a swipe/double tap gesture and a movement which is neither of them. so if you have a view full of buttons and other interactive stuff and user does a swipe or double tap gesture, it is guaranteed (although i believe there is no such thing as guarantee in life ;) ) that no other event callback will be called but only onSwipe() or onDoubleTap().
Anyway that's what i came up with to take the pain away in those circumstances when we just need to handle some simple Gestures.
hope it will be helpful for you and can make your life a bit easier.
Subscribe to:
Posts (Atom)













