mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2025-01-18 03:52:11 +01:00
Check in a joystick gadget class
This commit is contained in:
parent
4e7d8bffc4
commit
8f98383fa5
@ -0,0 +1,319 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DockPanel;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.view.animation.Animation.AnimationListener;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
public class DockPanel extends LinearLayout {
|
||||
|
||||
// =========================================
|
||||
// Private members
|
||||
// =========================================
|
||||
|
||||
private static final String TAG = "DockPanel";
|
||||
private DockPosition position;
|
||||
private int contentLayoutId;
|
||||
private int handleButtonDrawableId;
|
||||
private Boolean isOpen;
|
||||
private Boolean animationRunning;
|
||||
private FrameLayout contentPlaceHolder;
|
||||
private ImageButton toggleButton;
|
||||
private int animationDuration;
|
||||
|
||||
// =========================================
|
||||
// Constructors
|
||||
// =========================================
|
||||
|
||||
public DockPanel(Context context, int contentLayoutId,
|
||||
int handleButtonDrawableId, Boolean isOpen) {
|
||||
super(context);
|
||||
|
||||
this.contentLayoutId = contentLayoutId;
|
||||
this.handleButtonDrawableId = handleButtonDrawableId;
|
||||
this.isOpen = isOpen;
|
||||
|
||||
Init(null);
|
||||
}
|
||||
|
||||
public DockPanel(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
// to prevent from crashing the designer
|
||||
try {
|
||||
Init(attrs);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Initialization
|
||||
// =========================================
|
||||
|
||||
private void Init(AttributeSet attrs) {
|
||||
setDefaultValues(attrs);
|
||||
|
||||
createHandleToggleButton();
|
||||
|
||||
// create the handle container
|
||||
FrameLayout handleContainer = new FrameLayout(getContext());
|
||||
handleContainer.addView(toggleButton);
|
||||
|
||||
// create and populate the panel's container, and inflate it
|
||||
contentPlaceHolder = new FrameLayout(getContext());
|
||||
String infService = Context.LAYOUT_INFLATER_SERVICE;
|
||||
LayoutInflater li = (LayoutInflater) getContext().getSystemService(
|
||||
infService);
|
||||
li.inflate(contentLayoutId, contentPlaceHolder, true);
|
||||
|
||||
// setting the layout of the panel parameters according to the docking
|
||||
// position
|
||||
if (position == DockPosition.LEFT || position == DockPosition.RIGHT) {
|
||||
handleContainer.setLayoutParams(new LayoutParams(
|
||||
android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
android.view.ViewGroup.LayoutParams.FILL_PARENT, 1));
|
||||
contentPlaceHolder.setLayoutParams(new LayoutParams(
|
||||
android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
android.view.ViewGroup.LayoutParams.FILL_PARENT, 1));
|
||||
} else {
|
||||
handleContainer.setLayoutParams(new LayoutParams(
|
||||
android.view.ViewGroup.LayoutParams.FILL_PARENT,
|
||||
android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 1));
|
||||
contentPlaceHolder.setLayoutParams(new LayoutParams(
|
||||
android.view.ViewGroup.LayoutParams.FILL_PARENT,
|
||||
android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 1));
|
||||
}
|
||||
|
||||
// adding the view to the parent layout according to docking position
|
||||
if (position == DockPosition.RIGHT || position == DockPosition.BOTTOM) {
|
||||
this.addView(handleContainer);
|
||||
this.addView(contentPlaceHolder);
|
||||
} else {
|
||||
this.addView(contentPlaceHolder);
|
||||
this.addView(handleContainer);
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
contentPlaceHolder.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDefaultValues(AttributeSet attrs) {
|
||||
// set default values
|
||||
isOpen = true;
|
||||
animationRunning = false;
|
||||
animationDuration = 500;
|
||||
setPosition(DockPosition.RIGHT);
|
||||
|
||||
// Try to load values set by xml markup
|
||||
if (attrs != null) {
|
||||
String namespace = "http://com.MobileAnarchy.Android.Widgets";
|
||||
|
||||
animationDuration = attrs.getAttributeIntValue(namespace,
|
||||
"animationDuration", 500);
|
||||
contentLayoutId = attrs.getAttributeResourceValue(namespace,
|
||||
"contentLayoutId", 0);
|
||||
handleButtonDrawableId = attrs.getAttributeResourceValue(
|
||||
namespace, "handleButtonDrawableResourceId", 0);
|
||||
isOpen = attrs.getAttributeBooleanValue(namespace, "isOpen", true);
|
||||
|
||||
// Enums are a bit trickier (needs to be parsed)
|
||||
try {
|
||||
position = DockPosition.valueOf(attrs.getAttributeValue(
|
||||
namespace, "dockPosition").toUpperCase());
|
||||
setPosition(position);
|
||||
} catch (Exception ex) {
|
||||
// Docking to the left is the default behavior
|
||||
setPosition(DockPosition.LEFT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createHandleToggleButton() {
|
||||
toggleButton = new ImageButton(getContext());
|
||||
toggleButton.setPadding(0, 0, 0, 0);
|
||||
toggleButton.setLayoutParams(new FrameLayout.LayoutParams(
|
||||
android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
Gravity.CENTER));
|
||||
toggleButton.setBackgroundColor(Color.TRANSPARENT);
|
||||
toggleButton.setImageResource(handleButtonDrawableId);
|
||||
toggleButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
toggle();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setPosition(DockPosition position) {
|
||||
this.position = position;
|
||||
switch (position) {
|
||||
case TOP:
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
setGravity(Gravity.TOP);
|
||||
break;
|
||||
case RIGHT:
|
||||
setOrientation(LinearLayout.HORIZONTAL);
|
||||
setGravity(Gravity.RIGHT);
|
||||
break;
|
||||
case BOTTOM:
|
||||
setOrientation(LinearLayout.VERTICAL);
|
||||
setGravity(Gravity.BOTTOM);
|
||||
break;
|
||||
case LEFT:
|
||||
setOrientation(LinearLayout.HORIZONTAL);
|
||||
setGravity(Gravity.LEFT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public methods
|
||||
// =========================================
|
||||
|
||||
public int getAnimationDuration() {
|
||||
return animationDuration;
|
||||
}
|
||||
|
||||
public void setAnimationDuration(int milliseconds) {
|
||||
animationDuration = milliseconds;
|
||||
}
|
||||
|
||||
public Boolean getIsRunning() {
|
||||
return animationRunning;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
if (!animationRunning) {
|
||||
Log.d(TAG, "Opening...");
|
||||
|
||||
Animation animation = createShowAnimation();
|
||||
this.setAnimation(animation);
|
||||
animation.start();
|
||||
|
||||
isOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (!animationRunning) {
|
||||
Log.d(TAG, "Closing...");
|
||||
|
||||
Animation animation = createHideAnimation();
|
||||
this.setAnimation(animation);
|
||||
animation.start();
|
||||
isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void toggle() {
|
||||
if (isOpen) {
|
||||
close();
|
||||
} else {
|
||||
open();
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Private methods
|
||||
// =========================================
|
||||
|
||||
private Animation createHideAnimation() {
|
||||
Animation animation = null;
|
||||
switch (position) {
|
||||
case TOP:
|
||||
animation = new TranslateAnimation(0, 0, 0, -contentPlaceHolder
|
||||
.getHeight());
|
||||
break;
|
||||
case RIGHT:
|
||||
animation = new TranslateAnimation(0, contentPlaceHolder
|
||||
.getWidth(), 0, 0);
|
||||
break;
|
||||
case BOTTOM:
|
||||
animation = new TranslateAnimation(0, 0, 0, contentPlaceHolder
|
||||
.getHeight());
|
||||
break;
|
||||
case LEFT:
|
||||
animation = new TranslateAnimation(0, -contentPlaceHolder
|
||||
.getWidth(), 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
animation.setDuration(animationDuration);
|
||||
animation.setInterpolator(new AccelerateInterpolator());
|
||||
animation.setAnimationListener(new AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
animationRunning = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
contentPlaceHolder.setVisibility(View.GONE);
|
||||
animationRunning = false;
|
||||
}
|
||||
});
|
||||
return animation;
|
||||
}
|
||||
|
||||
private Animation createShowAnimation() {
|
||||
Animation animation = null;
|
||||
switch (position) {
|
||||
case TOP:
|
||||
animation = new TranslateAnimation(0, 0, -contentPlaceHolder
|
||||
.getHeight(), 0);
|
||||
break;
|
||||
case RIGHT:
|
||||
animation = new TranslateAnimation(contentPlaceHolder.getWidth(),
|
||||
0, 0, 0);
|
||||
break;
|
||||
case BOTTOM:
|
||||
animation = new TranslateAnimation(0, 0, contentPlaceHolder
|
||||
.getHeight(), 0);
|
||||
break;
|
||||
case LEFT:
|
||||
animation = new TranslateAnimation(-contentPlaceHolder.getWidth(),
|
||||
0, 0, 0);
|
||||
break;
|
||||
}
|
||||
Log.d(TAG, "Animation duration: " + animationDuration);
|
||||
animation.setDuration(animationDuration);
|
||||
animation.setInterpolator(new DecelerateInterpolator());
|
||||
animation.setAnimationListener(new AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
animationRunning = true;
|
||||
contentPlaceHolder.setVisibility(View.VISIBLE);
|
||||
Log.d(TAG, "\"Show\" Animation started");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
animationRunning = false;
|
||||
Log.d(TAG, "\"Show\" Animation ended");
|
||||
}
|
||||
});
|
||||
return animation;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DockPanel;
|
||||
|
||||
public enum DockPosition {
|
||||
TOP, BOTTOM, LEFT, RIGHT
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DragAndDrop;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnTouchListener;
|
||||
|
||||
public class DragAndDropManager {
|
||||
|
||||
// =========================================
|
||||
// Private members
|
||||
// =========================================
|
||||
|
||||
protected static final String TAG = "DragAndDropManager";
|
||||
private static DragAndDropManager instance = null;
|
||||
private ArrayList<DropZone> dropZones;
|
||||
private OnTouchListener originalTouchListener;
|
||||
private DragSurface dragSurface;
|
||||
private DraggableItem draggedItem;
|
||||
private DropZone activeDropZone;
|
||||
|
||||
// =========================================
|
||||
// Protected Constructor
|
||||
// =========================================
|
||||
|
||||
protected DragAndDropManager() {
|
||||
// Exists only to defeat instantiation.
|
||||
dropZones = new ArrayList<DropZone>();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public Properties
|
||||
// =========================================
|
||||
|
||||
public static DragAndDropManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new DragAndDropManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
if (dragSurface == null)
|
||||
return null;
|
||||
|
||||
return dragSurface.getContext();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public Methods
|
||||
// =========================================
|
||||
|
||||
public void init(DragSurface surface) {
|
||||
dragSurface = surface;
|
||||
clearDropZones();
|
||||
}
|
||||
|
||||
public void clearDropZones() {
|
||||
dropZones.clear();
|
||||
}
|
||||
|
||||
public void addDropZone(DropZone dropZone) {
|
||||
dropZones.add(dropZone);
|
||||
}
|
||||
|
||||
|
||||
public void startDragging(OnTouchListener originalListener, DraggableItem draggedItem) {
|
||||
originalTouchListener = originalListener;
|
||||
this.draggedItem = draggedItem;
|
||||
draggedItem.getSource().setOnTouchListener(new OnTouchListener() {
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
int[] location = new int[2];
|
||||
v.getLocationOnScreen(location);
|
||||
event.offsetLocation(location[0], location[1]);
|
||||
invalidateDropZones((int)event.getX(), (int)event.getY());
|
||||
return dragSurface.onTouchEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
dragSurface.startDragging(draggedItem);
|
||||
}
|
||||
|
||||
|
||||
// =========================================
|
||||
// Protected Methods
|
||||
// =========================================
|
||||
|
||||
protected void invalidateDropZones(int x, int y) {
|
||||
if (activeDropZone != null) {
|
||||
if (!activeDropZone.isOver(x, y)) {
|
||||
activeDropZone.getListener().OnDragZoneLeft(activeDropZone, draggedItem);
|
||||
activeDropZone = null;
|
||||
}
|
||||
else {
|
||||
// we are still over the same drop zone, no need to check other drop zones
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (DropZone dropZone : dropZones) {
|
||||
if (dropZone.isOver(x, y)) {
|
||||
activeDropZone = dropZone;
|
||||
dropZone.getListener().OnDragZoneEntered(activeDropZone, draggedItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void stoppedDragging() {
|
||||
if (activeDropZone != null) {
|
||||
activeDropZone.getListener().OnDropped(activeDropZone, draggedItem);
|
||||
}
|
||||
|
||||
// Registering the "old" listener to the view that initiated this drag session
|
||||
draggedItem.getSource().setOnTouchListener(originalTouchListener);
|
||||
draggedItem = null;
|
||||
activeDropZone = null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DragAndDrop;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class DragSurface extends FrameLayout {
|
||||
|
||||
private float draggedViewHalfHeight;
|
||||
private float draggedViewHalfWidth;
|
||||
private int framesCount;
|
||||
|
||||
private Boolean isDragging;
|
||||
private DraggableItem draggedItem;
|
||||
|
||||
public DragSurface(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
isDragging = false;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Touch Events Listener
|
||||
// =========================================
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (isDragging && event.getAction() == MotionEvent.ACTION_UP) {
|
||||
// Dragging ended
|
||||
removeAllViews();
|
||||
isDragging = false;
|
||||
|
||||
DragAndDropManager.getInstance().stoppedDragging();
|
||||
}
|
||||
|
||||
if (isDragging && event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
// Move the dragged view to it's new position
|
||||
repositionView(event.getX(), event.getY());
|
||||
|
||||
// Mark this event as handled (so that other UI elements will not intercept it)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void startDragging(DraggableItem draggableItem) {
|
||||
this.draggedItem = draggableItem;
|
||||
this.draggedItem.getDraggedView().setVisibility(INVISIBLE);
|
||||
isDragging = true;
|
||||
addView(this.draggedItem.getDraggedView());
|
||||
//repositionView(x, y);
|
||||
framesCount = 0;
|
||||
}
|
||||
|
||||
private void repositionView(float x, float y) {
|
||||
draggedViewHalfHeight = draggedItem.getDraggedView().getHeight() / 2f;
|
||||
draggedViewHalfWidth = draggedItem.getDraggedView().getWidth() / 2f;
|
||||
|
||||
// If the dragged view was not drawn yet, skip this phase
|
||||
if (draggedViewHalfHeight == 0 || draggedViewHalfWidth == 0)
|
||||
return;
|
||||
|
||||
framesCount++;
|
||||
|
||||
//Log.d(TAG, "Original = (x=" + x + ", y=" + y + ")");
|
||||
//Log.d(TAG, "Size (W=" + draggedViewHalfWidth + ", H=" + draggedViewHalfHeight + ")");
|
||||
|
||||
x = x - draggedViewHalfWidth;
|
||||
y = y - draggedViewHalfHeight;
|
||||
|
||||
x = Math.max(x, 0);
|
||||
x = Math.min(x, getWidth() - draggedViewHalfWidth * 2);
|
||||
|
||||
y = Math.max(y, 0);
|
||||
y = Math.min(y, getHeight() - draggedViewHalfHeight * 2);
|
||||
|
||||
//Log.d(TAG, "Moving view to (x=" + x + ", y=" + y + ")");
|
||||
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT, Gravity.TOP + Gravity.LEFT);
|
||||
|
||||
lp.setMargins((int)x, (int)y, 0, 0);
|
||||
draggedItem.getDraggedView().setLayoutParams(lp);
|
||||
|
||||
// hte first couple of dragged frame's positions are not calculated correctly,
|
||||
// so we have a threshold before making the dragged view visible again
|
||||
if (framesCount < 2)
|
||||
return;
|
||||
|
||||
draggedItem.getDraggedView().setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DragAndDrop;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public class DraggableItem {
|
||||
|
||||
// =========================================
|
||||
// Private members
|
||||
// =========================================
|
||||
|
||||
private View source;
|
||||
private View draggedView;
|
||||
private Object tag;
|
||||
|
||||
// =========================================
|
||||
// Constructor
|
||||
// =========================================
|
||||
|
||||
public DraggableItem(View source, View draggedItem) {
|
||||
this.source = source;
|
||||
this.draggedView = draggedItem;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public properties
|
||||
// =========================================
|
||||
|
||||
public Object getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTag(Object tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public View getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public View getDraggedView() {
|
||||
return draggedView;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DragAndDrop;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TableLayout.LayoutParams;
|
||||
|
||||
public class DraggableViewsFactory {
|
||||
|
||||
public static View getLabel(String text) {
|
||||
Context context = DragAndDropManager.getInstance().getContext();
|
||||
TextView textView = new TextView(context);
|
||||
textView.setText(text);
|
||||
textView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
|
||||
//textView.setGravity(Gravity.TOP + Gravity.LEFT);
|
||||
return textView;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DragAndDrop;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
public class DropZone {
|
||||
|
||||
// =========================================
|
||||
// Private members
|
||||
// =========================================
|
||||
|
||||
private View view;
|
||||
private DropZoneEventsListener listener;
|
||||
private int left, top, width, height;
|
||||
private Boolean dimansionsCalculated;
|
||||
|
||||
// =========================================
|
||||
// Constructor
|
||||
// =========================================
|
||||
|
||||
public DropZone(View view, DropZoneEventsListener listener) {
|
||||
this.view = view;
|
||||
this.listener = listener;
|
||||
dimansionsCalculated = false;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public properties
|
||||
// =========================================
|
||||
|
||||
public View getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public methods
|
||||
// =========================================
|
||||
|
||||
public Boolean isOver(int x, int y) {
|
||||
if (!dimansionsCalculated)
|
||||
calculateDimensions();
|
||||
|
||||
Boolean isOver = (x >= left && x <= (left + width)) &&
|
||||
(y >= top && y <= (top + height));
|
||||
|
||||
//Log.d("DragZone", "x=" +x + ", left=" + left + ", y=" + y + ", top=" + top + " width=" + width + ", height=" + height + ", isover=" + isOver);
|
||||
|
||||
return isOver;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Protected & Private methods
|
||||
// =========================================
|
||||
|
||||
protected DropZoneEventsListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
private void calculateDimensions() {
|
||||
int[] location = new int[2];
|
||||
view.getLocationOnScreen(location);
|
||||
left = location[0];
|
||||
top = location[1];
|
||||
width = view.getWidth();
|
||||
height = view.getHeight();
|
||||
dimansionsCalculated = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.MobileAnarchy.Android.Widgets.DragAndDrop;
|
||||
|
||||
public interface DropZoneEventsListener {
|
||||
|
||||
void OnDragZoneEntered(DropZone zone, DraggableItem item);
|
||||
void OnDragZoneLeft(DropZone zone, DraggableItem item);
|
||||
void OnDropped(DropZone zone, DraggableItem item);
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package com.MobileAnarchy.Android.Widgets.Joystick;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
public class DualJoystickView extends LinearLayout {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = DualJoystickView.class.getSimpleName();
|
||||
|
||||
private final boolean D = false;
|
||||
private Paint dbgPaint1;
|
||||
|
||||
private JoystickView stickL;
|
||||
private JoystickView stickR;
|
||||
|
||||
private View pad;
|
||||
|
||||
public DualJoystickView(Context context) {
|
||||
super(context);
|
||||
stickL = new JoystickView(context);
|
||||
stickR = new JoystickView(context);
|
||||
initDualJoystickView();
|
||||
}
|
||||
|
||||
public DualJoystickView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
stickL = new JoystickView(context, attrs);
|
||||
stickR = new JoystickView(context, attrs);
|
||||
initDualJoystickView();
|
||||
}
|
||||
|
||||
private void initDualJoystickView() {
|
||||
setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
if ( D ) {
|
||||
dbgPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
dbgPaint1.setColor(Color.CYAN);
|
||||
dbgPaint1.setStrokeWidth(1);
|
||||
dbgPaint1.setStyle(Paint.Style.STROKE);
|
||||
}
|
||||
|
||||
pad = new View(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
removeView(stickL);
|
||||
removeView(stickR);
|
||||
|
||||
float padW = getMeasuredWidth()-(getMeasuredHeight()*2);
|
||||
int joyWidth = (int) ((getMeasuredWidth()-padW)/2);
|
||||
LayoutParams joyLParams = new LayoutParams(joyWidth,getMeasuredHeight());
|
||||
|
||||
stickL.setLayoutParams(joyLParams);
|
||||
stickR.setLayoutParams(joyLParams);
|
||||
|
||||
stickL.TAG = "L";
|
||||
stickR.TAG = "R";
|
||||
stickL.setPointerId(JoystickView.INVALID_POINTER_ID);
|
||||
stickR.setPointerId(JoystickView.INVALID_POINTER_ID);
|
||||
|
||||
addView(stickL);
|
||||
|
||||
ViewGroup.LayoutParams padLParams = new ViewGroup.LayoutParams((int) padW,getMeasuredHeight());
|
||||
removeView(pad);
|
||||
pad.setLayoutParams(padLParams);
|
||||
addView(pad);
|
||||
|
||||
addView(stickR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
stickR.setTouchOffset(stickR.getLeft(), stickR.getTop());
|
||||
}
|
||||
|
||||
public void setAutoReturnToCenter(boolean left, boolean right) {
|
||||
stickL.setAutoReturnToCenter(left);
|
||||
stickR.setAutoReturnToCenter(right);
|
||||
}
|
||||
|
||||
public void setOnJostickMovedListener(JoystickMovedListener left, JoystickMovedListener right) {
|
||||
stickL.setOnJostickMovedListener(left);
|
||||
stickR.setOnJostickMovedListener(right);
|
||||
}
|
||||
|
||||
public void setOnJostickClickedListener(JoystickClickedListener left, JoystickClickedListener right) {
|
||||
stickL.setOnJostickClickedListener(left);
|
||||
stickR.setOnJostickClickedListener(right);
|
||||
}
|
||||
|
||||
public void setYAxisInverted(boolean leftYAxisInverted, boolean rightYAxisInverted) {
|
||||
stickL.setYAxisInverted(leftYAxisInverted);
|
||||
stickL.setYAxisInverted(rightYAxisInverted);
|
||||
}
|
||||
|
||||
public void setMovementConstraint(int movementConstraint) {
|
||||
stickL.setMovementConstraint(movementConstraint);
|
||||
stickR.setMovementConstraint(movementConstraint);
|
||||
}
|
||||
|
||||
public void setMovementRange(float movementRangeLeft, float movementRangeRight) {
|
||||
stickL.setMovementRange(movementRangeLeft);
|
||||
stickR.setMovementRange(movementRangeRight);
|
||||
}
|
||||
|
||||
public void setMoveResolution(float leftMoveResolution, float rightMoveResolution) {
|
||||
stickL.setMoveResolution(leftMoveResolution);
|
||||
stickR.setMoveResolution(rightMoveResolution);
|
||||
}
|
||||
|
||||
public void setUserCoordinateSystem(int leftCoordinateSystem, int rightCoordinateSystem) {
|
||||
stickL.setUserCoordinateSystem(leftCoordinateSystem);
|
||||
stickR.setUserCoordinateSystem(rightCoordinateSystem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
super.dispatchDraw(canvas);
|
||||
if (D) {
|
||||
canvas.drawRect(1, 1, getMeasuredWidth()-1, getMeasuredHeight()-1, dbgPaint1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
boolean l = stickL.dispatchTouchEvent(ev);
|
||||
boolean r = stickR.dispatchTouchEvent(ev);
|
||||
return l || r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
boolean l = stickL.onTouchEvent(ev);
|
||||
boolean r = stickR.onTouchEvent(ev);
|
||||
return l || r;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.MobileAnarchy.Android.Widgets.Joystick;
|
||||
|
||||
public interface JoystickClickedListener {
|
||||
public void OnClicked();
|
||||
public void OnReleased();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.MobileAnarchy.Android.Widgets.Joystick;
|
||||
|
||||
public interface JoystickMovedListener {
|
||||
public void OnMoved(int pan, int tilt);
|
||||
public void OnReleased();
|
||||
public void OnReturnedToCenter();
|
||||
}
|
@ -0,0 +1,521 @@
|
||||
package com.MobileAnarchy.Android.Widgets.Joystick;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
public class JoystickView extends View {
|
||||
public static final int INVALID_POINTER_ID = -1;
|
||||
|
||||
// =========================================
|
||||
// Private Members
|
||||
// =========================================
|
||||
private final boolean D = false;
|
||||
String TAG = "JoystickView";
|
||||
|
||||
private Paint dbgPaint1;
|
||||
private Paint dbgPaint2;
|
||||
|
||||
private Paint bgPaint;
|
||||
private Paint handlePaint;
|
||||
|
||||
private int innerPadding;
|
||||
private int bgRadius;
|
||||
private int handleRadius;
|
||||
private int movementRadius;
|
||||
private int handleInnerBoundaries;
|
||||
|
||||
private JoystickMovedListener moveListener;
|
||||
private JoystickClickedListener clickListener;
|
||||
|
||||
//# of pixels movement required between reporting to the listener
|
||||
private float moveResolution;
|
||||
|
||||
private boolean yAxisInverted;
|
||||
private boolean autoReturnToCenter;
|
||||
|
||||
//Max range of movement in user coordinate system
|
||||
public final static int CONSTRAIN_BOX = 0;
|
||||
public final static int CONSTRAIN_CIRCLE = 1;
|
||||
private int movementConstraint;
|
||||
private float movementRange;
|
||||
|
||||
public final static int COORDINATE_CARTESIAN = 0; //Regular cartesian coordinates
|
||||
public final static int COORDINATE_DIFFERENTIAL = 1; //Uses polar rotation of 45 degrees to calc differential drive paramaters
|
||||
private int userCoordinateSystem;
|
||||
|
||||
//Records touch pressure for click handling
|
||||
private float touchPressure;
|
||||
private boolean clicked;
|
||||
private float clickThreshold;
|
||||
|
||||
//Last touch point in view coordinates
|
||||
private int pointerId = INVALID_POINTER_ID;
|
||||
private float touchX, touchY;
|
||||
|
||||
//Last reported position in view coordinates (allows different reporting sensitivities)
|
||||
private float reportX, reportY;
|
||||
|
||||
//Handle center in view coordinates
|
||||
private float handleX, handleY;
|
||||
|
||||
//Center of the view in view coordinates
|
||||
private int cX, cY;
|
||||
|
||||
//Size of the view in view coordinates
|
||||
private int dimX, dimY;
|
||||
|
||||
//Cartesian coordinates of last touch point - joystick center is (0,0)
|
||||
private int cartX, cartY;
|
||||
|
||||
//Polar coordinates of the touch point from joystick center
|
||||
private double radial;
|
||||
private double angle;
|
||||
|
||||
//User coordinates of last touch point
|
||||
private int userX, userY;
|
||||
|
||||
//Offset co-ordinates (used when touch events are received from parent's coordinate origin)
|
||||
private int offsetX;
|
||||
private int offsetY;
|
||||
|
||||
// =========================================
|
||||
// Constructors
|
||||
// =========================================
|
||||
|
||||
public JoystickView(Context context) {
|
||||
super(context);
|
||||
initJoystickView();
|
||||
}
|
||||
|
||||
public JoystickView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initJoystickView();
|
||||
}
|
||||
|
||||
public JoystickView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initJoystickView();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Initialization
|
||||
// =========================================
|
||||
|
||||
private void initJoystickView() {
|
||||
setFocusable(true);
|
||||
|
||||
dbgPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
dbgPaint1.setColor(Color.RED);
|
||||
dbgPaint1.setStrokeWidth(1);
|
||||
dbgPaint1.setStyle(Paint.Style.STROKE);
|
||||
|
||||
dbgPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
dbgPaint2.setColor(Color.GREEN);
|
||||
dbgPaint2.setStrokeWidth(1);
|
||||
dbgPaint2.setStyle(Paint.Style.STROKE);
|
||||
|
||||
bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
bgPaint.setColor(Color.GRAY);
|
||||
bgPaint.setStrokeWidth(1);
|
||||
bgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
|
||||
handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
handlePaint.setColor(Color.DKGRAY);
|
||||
handlePaint.setStrokeWidth(1);
|
||||
handlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
|
||||
innerPadding = 10;
|
||||
|
||||
setMovementRange(10);
|
||||
setMoveResolution(1.0f);
|
||||
setClickThreshold(0.4f);
|
||||
setYAxisInverted(true);
|
||||
setUserCoordinateSystem(COORDINATE_CARTESIAN);
|
||||
setAutoReturnToCenter(true);
|
||||
}
|
||||
|
||||
public void setAutoReturnToCenter(boolean autoReturnToCenter) {
|
||||
this.autoReturnToCenter = autoReturnToCenter;
|
||||
}
|
||||
|
||||
public boolean isAutoReturnToCenter() {
|
||||
return autoReturnToCenter;
|
||||
}
|
||||
|
||||
public void setUserCoordinateSystem(int userCoordinateSystem) {
|
||||
if (userCoordinateSystem < COORDINATE_CARTESIAN || movementConstraint > COORDINATE_DIFFERENTIAL)
|
||||
Log.e(TAG, "invalid value for userCoordinateSystem");
|
||||
else
|
||||
this.userCoordinateSystem = userCoordinateSystem;
|
||||
}
|
||||
|
||||
public int getUserCoordinateSystem() {
|
||||
return userCoordinateSystem;
|
||||
}
|
||||
|
||||
public void setMovementConstraint(int movementConstraint) {
|
||||
if (movementConstraint < CONSTRAIN_BOX || movementConstraint > CONSTRAIN_CIRCLE)
|
||||
Log.e(TAG, "invalid value for movementConstraint");
|
||||
else
|
||||
this.movementConstraint = movementConstraint;
|
||||
}
|
||||
|
||||
public int getMovementConstraint() {
|
||||
return movementConstraint;
|
||||
}
|
||||
|
||||
public boolean isYAxisInverted() {
|
||||
return yAxisInverted;
|
||||
}
|
||||
|
||||
public void setYAxisInverted(boolean yAxisInverted) {
|
||||
this.yAxisInverted = yAxisInverted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pressure sensitivity for registering a click
|
||||
* @param clickThreshold threshold 0...1.0f inclusive. 0 will cause clicks to never be reported, 1.0 is a very hard click
|
||||
*/
|
||||
public void setClickThreshold(float clickThreshold) {
|
||||
if (clickThreshold < 0 || clickThreshold > 1.0f)
|
||||
Log.e(TAG, "clickThreshold must range from 0...1.0f inclusive");
|
||||
else
|
||||
this.clickThreshold = clickThreshold;
|
||||
}
|
||||
|
||||
public float getClickThreshold() {
|
||||
return clickThreshold;
|
||||
}
|
||||
|
||||
public void setMovementRange(float movementRange) {
|
||||
this.movementRange = movementRange;
|
||||
}
|
||||
|
||||
public float getMovementRange() {
|
||||
return movementRange;
|
||||
}
|
||||
|
||||
public void setMoveResolution(float moveResolution) {
|
||||
this.moveResolution = moveResolution;
|
||||
}
|
||||
|
||||
public float getMoveResolution() {
|
||||
return moveResolution;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public Methods
|
||||
// =========================================
|
||||
|
||||
public void setOnJostickMovedListener(JoystickMovedListener listener) {
|
||||
this.moveListener = listener;
|
||||
}
|
||||
|
||||
public void setOnJostickClickedListener(JoystickClickedListener listener) {
|
||||
this.clickListener = listener;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Drawing Functionality
|
||||
// =========================================
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
// Here we make sure that we have a perfect circle
|
||||
int measuredWidth = measure(widthMeasureSpec);
|
||||
int measuredHeight = measure(heightMeasureSpec);
|
||||
setMeasuredDimension(measuredWidth, measuredHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
|
||||
int d = Math.min(getMeasuredWidth(), getMeasuredHeight());
|
||||
|
||||
dimX = d;
|
||||
dimY = d;
|
||||
|
||||
cX = d / 2;
|
||||
cY = d / 2;
|
||||
|
||||
bgRadius = dimX/2 - innerPadding;
|
||||
handleRadius = (int)(d * 0.25);
|
||||
handleInnerBoundaries = handleRadius;
|
||||
movementRadius = Math.min(cX, cY) - handleInnerBoundaries;
|
||||
}
|
||||
|
||||
private int measure(int measureSpec) {
|
||||
int result = 0;
|
||||
// Decode the measurement specifications.
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
if (specMode == MeasureSpec.UNSPECIFIED) {
|
||||
// Return a default size of 200 if no bounds are specified.
|
||||
result = 200;
|
||||
} else {
|
||||
// As you want to fill the available space
|
||||
// always return the full available bounds.
|
||||
result = specSize;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.save();
|
||||
// Draw the background
|
||||
canvas.drawCircle(cX, cY, bgRadius, bgPaint);
|
||||
|
||||
// Draw the handle
|
||||
handleX = touchX + cX;
|
||||
handleY = touchY + cY;
|
||||
canvas.drawCircle(handleX, handleY, handleRadius, handlePaint);
|
||||
|
||||
if (D) {
|
||||
canvas.drawRect(1, 1, getMeasuredWidth()-1, getMeasuredHeight()-1, dbgPaint1);
|
||||
|
||||
canvas.drawCircle(handleX, handleY, 3, dbgPaint1);
|
||||
|
||||
if ( movementConstraint == CONSTRAIN_CIRCLE ) {
|
||||
canvas.drawCircle(cX, cY, this.movementRadius, dbgPaint1);
|
||||
}
|
||||
else {
|
||||
canvas.drawRect(cX-movementRadius, cY-movementRadius, cX+movementRadius, cY+movementRadius, dbgPaint1);
|
||||
}
|
||||
|
||||
//Origin to touch point
|
||||
canvas.drawLine(cX, cY, handleX, handleY, dbgPaint2);
|
||||
|
||||
int baseY = (int) (touchY < 0 ? cY + handleRadius : cY - handleRadius);
|
||||
canvas.drawText(String.format("%s (%.0f,%.0f)", TAG, touchX, touchY), handleX-20, baseY-7, dbgPaint2);
|
||||
canvas.drawText("("+ String.format("%.0f, %.1f", radial, angle * 57.2957795) + (char) 0x00B0 + ")", handleX-20, baseY+15, dbgPaint2);
|
||||
}
|
||||
|
||||
// Log.d(TAG, String.format("touch(%f,%f)", touchX, touchY));
|
||||
// Log.d(TAG, String.format("onDraw(%.1f,%.1f)\n\n", handleX, handleY));
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
// Constrain touch within a box
|
||||
private void constrainBox() {
|
||||
touchX = Math.max(Math.min(touchX, movementRadius), -movementRadius);
|
||||
touchY = Math.max(Math.min(touchY, movementRadius), -movementRadius);
|
||||
}
|
||||
|
||||
// Constrain touch within a circle
|
||||
private void constrainCircle() {
|
||||
float diffX = touchX;
|
||||
float diffY = touchY;
|
||||
double radial = Math.sqrt((diffX*diffX) + (diffY*diffY));
|
||||
if ( radial > movementRadius ) {
|
||||
touchX = (int)((diffX / radial) * movementRadius);
|
||||
touchY = (int)((diffY / radial) * movementRadius);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPointerId(int id) {
|
||||
this.pointerId = id;
|
||||
}
|
||||
|
||||
public int getPointerId() {
|
||||
return pointerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
final int action = ev.getAction();
|
||||
switch (action & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
return processMoveEvent(ev);
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_UP: {
|
||||
if ( pointerId != INVALID_POINTER_ID ) {
|
||||
// Log.d(TAG, "ACTION_UP");
|
||||
returnHandleToCenter();
|
||||
setPointerId(INVALID_POINTER_ID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_POINTER_UP: {
|
||||
if ( pointerId != INVALID_POINTER_ID ) {
|
||||
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
final int pointerId = ev.getPointerId(pointerIndex);
|
||||
if ( pointerId == this.pointerId ) {
|
||||
// Log.d(TAG, "ACTION_POINTER_UP: " + pointerId);
|
||||
returnHandleToCenter();
|
||||
setPointerId(INVALID_POINTER_ID);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
if ( pointerId == INVALID_POINTER_ID ) {
|
||||
int x = (int) ev.getX();
|
||||
if ( x >= offsetX && x < offsetX + dimX ) {
|
||||
setPointerId(ev.getPointerId(0));
|
||||
// Log.d(TAG, "ACTION_DOWN: " + getPointerId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
if ( pointerId == INVALID_POINTER_ID ) {
|
||||
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
||||
final int pointerId = ev.getPointerId(pointerIndex);
|
||||
int x = (int) ev.getX(pointerId);
|
||||
if ( x >= offsetX && x < offsetX + dimX ) {
|
||||
// Log.d(TAG, "ACTION_POINTER_DOWN: " + pointerId);
|
||||
setPointerId(pointerId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processMoveEvent(MotionEvent ev) {
|
||||
if ( pointerId != INVALID_POINTER_ID ) {
|
||||
final int pointerIndex = ev.findPointerIndex(pointerId);
|
||||
|
||||
// Translate touch position to center of view
|
||||
float x = ev.getX(pointerIndex);
|
||||
touchX = x - cX - offsetX;
|
||||
float y = ev.getY(pointerIndex);
|
||||
touchY = y - cY - offsetY;
|
||||
|
||||
// Log.d(TAG, String.format("ACTION_MOVE: (%03.0f, %03.0f) => (%03.0f, %03.0f)", x, y, touchX, touchY));
|
||||
|
||||
reportOnMoved();
|
||||
invalidate();
|
||||
|
||||
touchPressure = ev.getPressure(pointerIndex);
|
||||
reportOnPressure();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void reportOnMoved() {
|
||||
if ( movementConstraint == CONSTRAIN_CIRCLE )
|
||||
constrainCircle();
|
||||
else
|
||||
constrainBox();
|
||||
|
||||
calcUserCoordinates();
|
||||
|
||||
if (moveListener != null) {
|
||||
boolean rx = Math.abs(touchX - reportX) >= moveResolution;
|
||||
boolean ry = Math.abs(touchY - reportY) >= moveResolution;
|
||||
if (rx || ry) {
|
||||
this.reportX = touchX;
|
||||
this.reportY = touchY;
|
||||
|
||||
// Log.d(TAG, String.format("moveListener.OnMoved(%d,%d)", (int)userX, (int)userY));
|
||||
moveListener.OnMoved(userX, userY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void calcUserCoordinates() {
|
||||
//First convert to cartesian coordinates
|
||||
cartX = (int)(touchX / movementRadius * movementRange);
|
||||
cartY = (int)(touchY / movementRadius * movementRange);
|
||||
|
||||
radial = Math.sqrt((cartX*cartX) + (cartY*cartY));
|
||||
angle = Math.atan2(cartY, cartX);
|
||||
|
||||
//Invert Y axis if requested
|
||||
if ( !yAxisInverted )
|
||||
cartY *= -1;
|
||||
|
||||
if ( userCoordinateSystem == COORDINATE_CARTESIAN ) {
|
||||
userX = cartX;
|
||||
userY = cartY;
|
||||
}
|
||||
else if ( userCoordinateSystem == COORDINATE_DIFFERENTIAL ) {
|
||||
userX = cartY + cartX / 4;
|
||||
userY = cartY - cartX / 4;
|
||||
|
||||
if ( userX < -movementRange )
|
||||
userX = (int)-movementRange;
|
||||
if ( userX > movementRange )
|
||||
userX = (int)movementRange;
|
||||
|
||||
if ( userY < -movementRange )
|
||||
userY = (int)-movementRange;
|
||||
if ( userY > movementRange )
|
||||
userY = (int)movementRange;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Simple pressure click
|
||||
private void reportOnPressure() {
|
||||
// Log.d(TAG, String.format("touchPressure=%.2f", this.touchPressure));
|
||||
if ( clickListener != null ) {
|
||||
if ( clicked && touchPressure < clickThreshold ) {
|
||||
clickListener.OnReleased();
|
||||
this.clicked = false;
|
||||
// Log.d(TAG, "reset click");
|
||||
invalidate();
|
||||
}
|
||||
else if ( !clicked && touchPressure >= clickThreshold ) {
|
||||
clicked = true;
|
||||
clickListener.OnClicked();
|
||||
// Log.d(TAG, "click");
|
||||
invalidate();
|
||||
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void returnHandleToCenter() {
|
||||
if ( autoReturnToCenter ) {
|
||||
final int numberOfFrames = 5;
|
||||
final double intervalsX = (0 - touchX) / numberOfFrames;
|
||||
final double intervalsY = (0 - touchY) / numberOfFrames;
|
||||
|
||||
for (int i = 0; i < numberOfFrames; i++) {
|
||||
final int j = i;
|
||||
postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
touchX += intervalsX;
|
||||
touchY += intervalsY;
|
||||
|
||||
reportOnMoved();
|
||||
invalidate();
|
||||
|
||||
if (moveListener != null && j == numberOfFrames - 1) {
|
||||
moveListener.OnReturnedToCenter();
|
||||
}
|
||||
}
|
||||
}, i * 40);
|
||||
}
|
||||
|
||||
if (moveListener != null) {
|
||||
moveListener.OnReleased();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTouchOffset(int x, int y) {
|
||||
offsetX = x;
|
||||
offsetY = y;
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package com.MobileAnarchy.Android.Widgets.ThresholdEditText;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.EditText;
|
||||
|
||||
public class ThresholdEditText extends EditText {
|
||||
|
||||
// =========================================
|
||||
// Private members
|
||||
// =========================================
|
||||
|
||||
private int threshold;
|
||||
private ThresholdTextChanged thresholdTextChanged;
|
||||
private Handler handler;
|
||||
private Runnable invoker;
|
||||
private boolean thresholdDisabledOnEmptyInput;
|
||||
|
||||
|
||||
// =========================================
|
||||
// Constructors
|
||||
// =========================================
|
||||
|
||||
public ThresholdEditText(Context context) {
|
||||
super(context);
|
||||
initAttributes(null);
|
||||
init();
|
||||
}
|
||||
|
||||
public ThresholdEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initAttributes(attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
// =========================================
|
||||
// Public properties
|
||||
// =========================================
|
||||
|
||||
/**
|
||||
* Get the current threshold value
|
||||
*/
|
||||
public int getThreshold() {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the threshold value (in milliseconds)
|
||||
*
|
||||
* @param threshold
|
||||
* Threshold value
|
||||
*/
|
||||
public void setThreshold(int threshold) {
|
||||
this.threshold = threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True = the callback will fire immediately when the content of the
|
||||
* EditText is emptied False = The threshold will be used even on
|
||||
* empty input
|
||||
*/
|
||||
public boolean getThresholdDisabledOnEmptyInput() {
|
||||
return thresholdDisabledOnEmptyInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param thresholdDisabledOnEmptyInput
|
||||
* Set to true if you want the callback to fire immediately when
|
||||
* the content of the EditText is emptied
|
||||
*/
|
||||
public void setThresholdDisabledOnEmptyInput(
|
||||
boolean thresholdDisabledOnEmptyInput) {
|
||||
this.thresholdDisabledOnEmptyInput = thresholdDisabledOnEmptyInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback to the OnThresholdTextChanged event
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void setOnThresholdTextChanged(ThresholdTextChanged listener) {
|
||||
this.thresholdTextChanged = listener;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Private / Protected methods
|
||||
// =========================================
|
||||
|
||||
/**
|
||||
* Load properties values from xml layout
|
||||
*/
|
||||
private void initAttributes(AttributeSet attrs) {
|
||||
if (attrs != null) {
|
||||
String namespace = "http://com.MobileAnarchy.Android.Widgets";
|
||||
|
||||
// Load values to local members
|
||||
this.threshold = attrs.getAttributeIntValue(namespace, "threshold",
|
||||
500);
|
||||
this.thresholdDisabledOnEmptyInput = attrs.getAttributeBooleanValue(
|
||||
namespace, "disableThresholdOnEmptyInput", true);
|
||||
} else {
|
||||
// Default threshold value is 0.5 seconds
|
||||
threshold = 500;
|
||||
|
||||
// Default behaviour on emptied text - no threshold
|
||||
thresholdDisabledOnEmptyInput = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the private members with default values
|
||||
*/
|
||||
private void init() {
|
||||
|
||||
handler = new Handler();
|
||||
|
||||
invoker = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
invokeCallback();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.addTextChangedListener(new TextWatcher() {
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||
int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before,
|
||||
int count) {
|
||||
|
||||
// Remove any existing pending callbacks
|
||||
handler.removeCallbacks(invoker);
|
||||
|
||||
if (s.length() == 0 && thresholdDisabledOnEmptyInput) {
|
||||
// The text is empty, so invoke the callback immediately
|
||||
invoker.run();
|
||||
} else {
|
||||
// Post a new delayed callback
|
||||
handler.postDelayed(invoker, threshold);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoking the callback on the listener provided (if provided)
|
||||
*/
|
||||
private void invokeCallback() {
|
||||
if (thresholdTextChanged != null) {
|
||||
thresholdTextChanged.onThersholdTextChanged(this.getText());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.MobileAnarchy.Android.Widgets.ThresholdEditText;
|
||||
|
||||
import android.text.Editable;
|
||||
|
||||
public interface ThresholdTextChanged {
|
||||
void onThersholdTextChanged(Editable text);
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package com.MobileAnarchy.Android.Widgets.TilesLayout;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class SingleTileLayout extends FrameLayout {
|
||||
|
||||
// =========================================
|
||||
// Private members
|
||||
// =========================================
|
||||
|
||||
private TilePosition position;
|
||||
private long timestamp;
|
||||
|
||||
// =========================================
|
||||
// Constructors
|
||||
// =========================================
|
||||
|
||||
public SingleTileLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SingleTileLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
|
||||
// =========================================
|
||||
// Public Methods
|
||||
// =========================================
|
||||
|
||||
public TilePosition getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(TilePosition position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Overrides
|
||||
// =========================================
|
||||
|
||||
@Override
|
||||
public void addView(View child) {
|
||||
super.addView(child);
|
||||
timestamp = java.lang.System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllViews() {
|
||||
super.removeAllViews();
|
||||
timestamp = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeView(View view) {
|
||||
super.removeView(view);
|
||||
timestamp = 0;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.MobileAnarchy.Android.Widgets.TilesLayout;
|
||||
|
||||
|
||||
public class TilePosition {
|
||||
|
||||
// =========================================
|
||||
// Private Members
|
||||
// =========================================
|
||||
|
||||
private float x, y, height, width;
|
||||
|
||||
// =========================================
|
||||
// Constructors
|
||||
// =========================================
|
||||
|
||||
public TilePosition(float x, float y, float width, float height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public Properties
|
||||
// =========================================
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public float getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public float getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
|
||||
// =========================================
|
||||
// Public Methods
|
||||
// =========================================
|
||||
|
||||
public Boolean equals(TilePosition position) {
|
||||
if (position == null)
|
||||
return false;
|
||||
|
||||
return this.x == position.x &&
|
||||
this.y == position.y &&
|
||||
this.height == position.height &&
|
||||
this.width == position.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TilePosition = [X: " + x + ", Y: " + y + ", Height: " + height + ", Width: " + width + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,299 @@
|
||||
package com.MobileAnarchy.Android.Widgets.TilesLayout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.R;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationSet;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.ScaleAnimation;
|
||||
import android.view.animation.TranslateAnimation;
|
||||
import android.view.animation.Animation.AnimationListener;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class TilesLayout extends FrameLayout {
|
||||
|
||||
// =========================================
|
||||
// Private members
|
||||
// =========================================
|
||||
|
||||
private static final String TAG = "TilesLayout";
|
||||
private int animatedTransitionDuration;
|
||||
private List<SingleTileLayout> tiles;
|
||||
private TilesLayoutPreset preset;
|
||||
private int tileBackgroundResourceId;
|
||||
|
||||
// =========================================
|
||||
// Constructors
|
||||
// =========================================
|
||||
|
||||
public TilesLayout(Context context) {
|
||||
super(context);
|
||||
Init(null);
|
||||
}
|
||||
|
||||
public TilesLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
Init(attrs);
|
||||
}
|
||||
|
||||
|
||||
// =========================================
|
||||
// Public Properties
|
||||
// =========================================
|
||||
|
||||
public void setPreset(TilesLayoutPreset preset) {
|
||||
try {
|
||||
rebuildLayout(preset);
|
||||
this.preset = preset;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Log.e(TAG, "Failed to set layout preset", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public TilesLayoutPreset getPreset() {
|
||||
return this.preset;
|
||||
}
|
||||
|
||||
public int getAnimatedTransitionDuration() {
|
||||
return animatedTransitionDuration;
|
||||
}
|
||||
|
||||
public void setAnimatedTransitionDuration(int animatedTransitionDuration) {
|
||||
this.animatedTransitionDuration = animatedTransitionDuration;
|
||||
}
|
||||
|
||||
public int getTileBackgroundResourceId() {
|
||||
return tileBackgroundResourceId;
|
||||
}
|
||||
|
||||
public void setTileBackgroundResourceId(int tileBackgroundResourceId) {
|
||||
this.tileBackgroundResourceId = tileBackgroundResourceId;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public Methods
|
||||
// =========================================
|
||||
|
||||
public void addContent(View view) {
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
if (tiles.get(i).getChildCount() == 0) {
|
||||
tiles.get(i).addView(view);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// No available space for the new view...
|
||||
// TODO: Take the tile with the smallest time stamp and place the new view in it
|
||||
}
|
||||
|
||||
public void clearView(int tileId) {
|
||||
if (tiles.size() < tileId) {
|
||||
tiles.get(tileId).removeAllViews();
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Private Methods
|
||||
// =========================================
|
||||
|
||||
private void Init(AttributeSet attrs) {
|
||||
animatedTransitionDuration = 750;
|
||||
tileBackgroundResourceId = R.drawable.edit_text;
|
||||
tiles = new ArrayList<SingleTileLayout>();
|
||||
}
|
||||
|
||||
|
||||
private void rebuildLayout(TilesLayoutPreset preset) {
|
||||
ArrayList<TilePosition> positions = buildViewsPositions(preset);
|
||||
|
||||
// We need to transform the current layout, to the new layout
|
||||
int extraViews = tiles.size() - positions.size();
|
||||
if (extraViews > 0) {
|
||||
// Remove the extra views
|
||||
while(tiles.size() - positions.size() > 0) {
|
||||
int lastViewPosition = tiles.size() - 1;
|
||||
removeView(tiles.get(lastViewPosition));
|
||||
tiles.remove(lastViewPosition);
|
||||
}
|
||||
} else {
|
||||
// Add the extra views
|
||||
for (int i = tiles.size(); i< positions.size(); i++) {
|
||||
TilePosition newTilePosition = positions.get(i);
|
||||
SingleTileLayout tile = new SingleTileLayout(getContext());
|
||||
tile.setBackgroundResource(tileBackgroundResourceId);
|
||||
|
||||
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
|
||||
(int)newTilePosition.getWidth(),
|
||||
(int)newTilePosition.getHeight(),
|
||||
Gravity.TOP + Gravity.LEFT);
|
||||
lp.setMargins((int)newTilePosition.getX(),
|
||||
(int)newTilePosition.getY(), 0, 0);
|
||||
|
||||
tile.setLayoutParams(lp);
|
||||
|
||||
tiles.add(tile);
|
||||
addView(tile);
|
||||
}
|
||||
}
|
||||
// There is a bug in the animation-set, so we'll not animate
|
||||
animateChange(positions);
|
||||
|
||||
// Regular repositioning (no animation)
|
||||
//processChange(positions);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private ArrayList<TilePosition> buildViewsPositions(TilesLayoutPreset preset) {
|
||||
int width = getWidth();
|
||||
int height = getHeight();
|
||||
|
||||
Log.d(TAG, "Container's Dimensions = Width: " + width + ", Height: " + height);
|
||||
ArrayList<TilePosition> actualPositions = new ArrayList<TilePosition>();
|
||||
for (TilePosition position : preset.getTilePositions()) {
|
||||
|
||||
int tileX = (int) Math.round(width * ((float)position.getX() / 100.0));
|
||||
int tileY = (int) Math.round(height * ((float)position.getY() / 100.0));
|
||||
int tileWidth = (int) Math.round(width * ((float)position.getWidth() / 100.0));
|
||||
int tileHeight = (int) Math.round(height * ((float)position.getHeight() / 100.0));
|
||||
|
||||
TilePosition actualPosition = new TilePosition(tileX, tileY, tileWidth, tileHeight);
|
||||
actualPositions.add(actualPosition);
|
||||
|
||||
Log.d(TAG, "New tile created - X: " + tileX + ", Y: " + tileY + ", Width: " + tileWidth + ", Height: " + tileHeight);
|
||||
}
|
||||
return actualPositions;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void processChange(ArrayList<TilePosition> positions) {
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
final SingleTileLayout currentTile = tiles.get(i);
|
||||
final TilePosition targetPosition = positions.get(i);
|
||||
currentTile.setPosition(targetPosition);
|
||||
|
||||
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
|
||||
(int)targetPosition.getWidth(),
|
||||
(int)targetPosition.getHeight(),
|
||||
Gravity.TOP + Gravity.LEFT);
|
||||
lp.setMargins((int)targetPosition.getX(),
|
||||
(int)targetPosition.getY(), 0, 0);
|
||||
|
||||
currentTile.setLayoutParams(lp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void animateChange(ArrayList<TilePosition> positions) {
|
||||
AnimationSet animationSet = new AnimationSet(true);
|
||||
DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
|
||||
|
||||
for (int i = 0; i < tiles.size(); i++) {
|
||||
AnimationSet scaleAndMove = new AnimationSet(true);
|
||||
scaleAndMove.setFillAfter(true);
|
||||
|
||||
final SingleTileLayout currentTile = tiles.get(i);
|
||||
TilePosition currentPosition = currentTile.getPosition();
|
||||
final TilePosition targetPosition = positions.get(i);
|
||||
|
||||
if (currentPosition == null) {
|
||||
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
|
||||
alphaAnimation.setDuration(animatedTransitionDuration);
|
||||
alphaAnimation.setStartOffset(0);
|
||||
currentTile.setAnimation(alphaAnimation);
|
||||
|
||||
scaleAndMove.addAnimation(alphaAnimation);
|
||||
}
|
||||
|
||||
currentTile.setPosition(targetPosition);
|
||||
|
||||
if (!targetPosition.equals(currentPosition)) {
|
||||
float toXDelta = 0, toYDelta = 0;
|
||||
if (currentPosition != null) {
|
||||
// Calculate new position
|
||||
toXDelta = targetPosition.getX() - currentPosition.getX();
|
||||
toYDelta = targetPosition.getY() - currentPosition.getY();
|
||||
|
||||
// Factor in the scaling animation
|
||||
toXDelta = toXDelta / (targetPosition.getWidth() / currentPosition.getWidth());
|
||||
toYDelta = toYDelta / (targetPosition.getHeight() / currentPosition.getHeight());
|
||||
}
|
||||
|
||||
// Move
|
||||
TranslateAnimation moveAnimation = new TranslateAnimation(0, toXDelta, 0, toYDelta);
|
||||
moveAnimation.setDuration(animatedTransitionDuration);
|
||||
moveAnimation.setStartOffset(0);
|
||||
moveAnimation.setFillAfter(true);
|
||||
moveAnimation.setInterpolator(decelerateInterpolator);
|
||||
scaleAndMove.addAnimation(moveAnimation);
|
||||
|
||||
// Physically move the tile when the animation ends
|
||||
scaleAndMove.setAnimationListener(new AnimationListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) { }
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) { }
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
|
||||
(int)targetPosition.getWidth(),
|
||||
(int)targetPosition.getHeight(),
|
||||
Gravity.TOP + Gravity.LEFT);
|
||||
lp.setMargins((int)targetPosition.getX(),
|
||||
(int)targetPosition.getY(), 0, 0);
|
||||
|
||||
currentTile.setLayoutParams(lp);
|
||||
|
||||
// The following null animation just gets rid of screen flicker
|
||||
animation = new TranslateAnimation(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
animation.setDuration(1);
|
||||
currentTile.startAnimation(animation);
|
||||
}
|
||||
});
|
||||
|
||||
// Scale
|
||||
if (currentPosition != null) {
|
||||
ScaleAnimation scaleAnimation =
|
||||
new ScaleAnimation(1,
|
||||
targetPosition.getWidth() / currentPosition.getWidth(),
|
||||
1,
|
||||
targetPosition.getHeight() / currentPosition.getHeight(),
|
||||
Animation.ABSOLUTE, 0,
|
||||
Animation.ABSOLUTE, 0);
|
||||
|
||||
scaleAnimation.setDuration(animatedTransitionDuration);
|
||||
scaleAnimation.setStartOffset(0);
|
||||
scaleAnimation.setFillAfter(true);
|
||||
scaleAndMove.addAnimation(scaleAnimation);
|
||||
}
|
||||
|
||||
// Set animation to the tile
|
||||
currentTile.setAnimation(scaleAndMove);
|
||||
}
|
||||
// Add to the total animation set
|
||||
animationSet.addAnimation(scaleAndMove);
|
||||
}
|
||||
|
||||
if (animationSet.getAnimations().size() > 0) {
|
||||
Log.d(TAG, "Starting animation");
|
||||
animationSet.setFillAfter(true);
|
||||
animationSet.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package com.MobileAnarchy.Android.Widgets.TilesLayout;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Describes the positioning of a tiles in a 100x100 environment
|
||||
*/
|
||||
public class TilesLayoutPreset {
|
||||
|
||||
// =========================================
|
||||
// Private Members
|
||||
// =========================================
|
||||
|
||||
private List<TilePosition> _positions;
|
||||
private String _presetName;
|
||||
|
||||
// =========================================
|
||||
// Constructors
|
||||
// =========================================
|
||||
|
||||
public TilesLayoutPreset(String name) {
|
||||
_presetName = name;
|
||||
_positions = new LinkedList<TilePosition>();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Public Methods
|
||||
// =========================================
|
||||
|
||||
public void add(float x, float y, float width, float height) {
|
||||
TilePosition tilePosition = new TilePosition(x, y, height, width);
|
||||
add(tilePosition);
|
||||
}
|
||||
|
||||
public void add(TilePosition tilePosition) {
|
||||
_positions.add(tilePosition);
|
||||
}
|
||||
|
||||
public Iterable<TilePosition> getTilePositions() {
|
||||
return _positions;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return _positions.size();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Static Presets Factories
|
||||
// =========================================
|
||||
|
||||
public static TilesLayoutPreset get1x1() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 1X1");
|
||||
preset.add(0, 0, 100, 100);
|
||||
return preset;
|
||||
}
|
||||
|
||||
|
||||
public static TilesLayoutPreset get1x2() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 1X2");
|
||||
preset.add(0, 0, 50, 100);
|
||||
preset.add(0, 50, 50, 100);
|
||||
return preset;
|
||||
}
|
||||
|
||||
|
||||
public static TilesLayoutPreset get2x1() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 2X1");
|
||||
preset.add(0, 0, 100, 50);
|
||||
preset.add(50, 0, 100, 50);
|
||||
return preset;
|
||||
}
|
||||
|
||||
|
||||
public static TilesLayoutPreset get2x2() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 2X2");
|
||||
preset.add(0, 0, 50, 50);
|
||||
preset.add(50, 0, 50, 50);
|
||||
preset.add(0, 50, 50, 50);
|
||||
preset.add(50, 50, 50, 50);
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static TilesLayoutPreset get3x3() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 3X3");
|
||||
preset.add(0, 0, 100/3f, 100/3f);
|
||||
preset.add(100/3f, 0, 100/3f, 100/3f);
|
||||
preset.add(200/3f, 0, 100/3f, 100/3f);
|
||||
preset.add(0, 100/3f, 100/3f, 100/3f);
|
||||
preset.add(100/3f, 100/3f, 100/3f, 100/3f);
|
||||
preset.add(200/3f, 100/3f, 100/3f, 100/3f);
|
||||
preset.add(0, 200/3f, 100/3f, 100/3f);
|
||||
preset.add(100/3f, 200/3f, 100/3f, 100/3f);
|
||||
preset.add(200/3f, 200/3f, 100/3f, 100/3f);
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static TilesLayoutPreset get3x2() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 3X2");
|
||||
preset.add(0, 0, 50, 100/3f);
|
||||
preset.add(100/3f, 0, 50, 100/3f);
|
||||
preset.add(200/3f, 0, 50, 100/3f);
|
||||
preset.add(0, 50, 50, 100/3f);
|
||||
preset.add(100/3f, 50, 50, 100/3f);
|
||||
preset.add(200/3f, 50, 50, 100/3f);
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static TilesLayoutPreset get2x3() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 2X3");
|
||||
preset.add(0, 0, 100/3f, 50);
|
||||
preset.add(50, 0, 100/3f, 50);
|
||||
preset.add(0, 100/3f, 100/3f, 50);
|
||||
preset.add(50, 100/3f, 100/3f, 50);
|
||||
preset.add(0, 200/3f, 100/3f, 50);
|
||||
preset.add(50, 200/3f, 100/3f, 50);
|
||||
return preset;
|
||||
}
|
||||
|
||||
|
||||
public static TilesLayoutPreset get4x4() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Default 2X2");
|
||||
preset.add(0, 0, 25, 25);
|
||||
preset.add(25, 0, 25, 25);
|
||||
preset.add(50, 0, 25, 25);
|
||||
preset.add(75, 0, 25, 25);
|
||||
preset.add(0, 25, 25, 25);
|
||||
preset.add(25, 25, 25, 25);
|
||||
preset.add(50, 25, 25, 25);
|
||||
preset.add(75, 25, 25, 25);
|
||||
preset.add(0, 50, 25, 25);
|
||||
preset.add(25, 50, 25, 25);
|
||||
preset.add(50, 50, 25, 25);
|
||||
preset.add(75, 50, 25, 25);
|
||||
preset.add(0, 75, 25, 25);
|
||||
preset.add(25, 75, 25, 25);
|
||||
preset.add(50, 75, 25, 25);
|
||||
preset.add(75, 75, 25, 25);
|
||||
return preset;
|
||||
}
|
||||
|
||||
public static TilesLayoutPreset get2x3x3() {
|
||||
TilesLayoutPreset preset = new TilesLayoutPreset("Custom 2X4X4");
|
||||
preset.add(0, 0, 50, 50);
|
||||
preset.add(50, 0, 50, 50);
|
||||
preset.add(0, 50, 50, 50);
|
||||
|
||||
preset.add(50, 50, 25, 25);
|
||||
preset.add(75, 50, 25, 25);
|
||||
preset.add(50, 75, 25, 25);
|
||||
preset.add(75, 75, 25, 25);
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user