Hello friends,
I draw a line using canvas.drawPath(mPath, mPaint); method.
but having some problem, i want to get whether line intersect with itself or not.
so please help.
thanks in advance
For better performance and UI response create a separate Thread with a Handler and Looper. Also use a HashSet to store the path which tells you of an intersection when you add a position since add() returns a boolean.
Here is some code which should hopefully give better performance.
public class MainActivity extends Activity {
private Handler mHandler;
private String position, lastPosition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onResume() {
super.onResume();
Tracker t = new Tracker();
t.start();
}
#Override
protected void onPause() {
if (mHandler != null)
mHandler.getLooper().quit();
super.onPause();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_MOVE:
position = event.getX()+","+event.getY();
if (position.equals(lastPosition)) break;
if (mHandler != null) {
Message msg = Message.obtain();
msg.obj = position;
mHandler.sendMessage(msg);
}
lastPosition = position;
break;
}
return true;
}
private class Tracker extends Thread {
HashSet<String> path = new HashSet<String>();
#Override
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
String position = String.valueOf(msg.obj);
if (!path.add(position))
Log.w("Intersection", position);//Handle the intersection
}
};
Looper.loop();
}
}
}
save the coordinates from events
#Override
public boolean onTouchEvent(MotionEvent event) {
event.getX();event.getY()`
}
to a Hashmap and compare
I think you're best bet would be to:
1) Keep one path representing what's currently on the canvas, and every time you use canvas.drawPath(nextPath), also add it to your global path and do something like globalPath.addPath(nextPath)
2) Take a look at this post: Collision detection with bitmaps on SurfaceView's canvas in Android. It seems that you should be able to compare the globalPath to the nextPath and tell if they ever collide.
3) If instead you wanted to just know if a single path collides with itself and don't care about adding new paths or anything, we'd need more information on how you're drawing this path. With lines, arcs, circles??
Related
My static handler has a WeakReference to my Activity (this is to prevent the well documented memory leak issue).
I post a long delayed message and I want this message delivered to my activity (which should be in the foreground).
My concern is that on orientation change, my activity is destroyed and the handler has a reference to the old activity which should have been destroyed.
In order to get around this in my onCreate for the activity I do this.
if(mHandler == null)
mHandler = new LoginHandler(this);
else {
mHandler.setTarget(this);
}
And my handler is declared as a static global variable:
private static LoginHandler mHandler = null;
and the implementing class is also static as below:
private static class LoginHandler extends Handler {
private WeakReference<LoginActivity> mTarget;
LoginHandler(LoginActivity target) {
mTarget = new WeakReference<LoginActivity>(target);
}
public void setTarget(LoginActivity target) {
mTarget = new WeakReference<LoginActivity>(target);
}
#Override
public void handleMessage(Message msg) {
// process incoming messages here
LoginActivity activity = mTarget.get();
switch (msg.what) {
case Constants.SUCCESS:
activity.doSomething();
break;
default:
activity.setStatusMessage("failed " + msg.obj, STATUS_TYPE_DONE);
}
}
}
What I want to know is if there is something wrong with changing the WeakReference on onCreate or is there anything else wrong with this approach?
Thanks,
So I wrote the following test to figure out whether I had the right idea or not and it seems that m approach is correct. In onCreate we change the WeakReference and the posted message will always get delivered to the activity that is in the foreground. If you change this code to always create a new Handler in onCreate you'll notice the update messages do not get delivered.
public class MainActivity extends Activity {
private static int COUNT = 0;
static LoginHandler mHandler;
private static class LoginHandler extends Handler {
private WeakReference<MainActivity> mTarget;
LoginHandler(MainActivity target) {
mTarget = new WeakReference<MainActivity>(target);
}
public void setTarget(MainActivity target) {
mTarget.clear();
mTarget = new WeakReference<MainActivity>(target);
}
#Override
public void handleMessage(Message msg) {
// int duration = Toast.LENGTH_LONG;
// process incoming messages here
MainActivity activity = mTarget.get();
activity.update(msg.arg1);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mHandler == null)
mHandler = new LoginHandler(this);
else
mHandler.setTarget(this);
((Button)findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Message msg = new Message();
msg.arg1 = COUNT++;
mHandler.sendMessageDelayed(msg, 3000);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void update(int count) {
((TextView) findViewById(R.id.hello_world)).setText("Hello World # "+ count);
}
}
A solution in getting away with activity's destroy-and-create life cycle, if you want to retain the active objects is to make use of the "Retent Fragments".
The idea is simple, you are telling the Android system to " retain" your fragment, when it's associated activity is being destroyed and re created. And make sure you grab the current activity's context in the fragment's onAttach() callable, so you are always updating the correct activity.
Below link has more details:
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
I have started venturing into a bit of Canvas stuff and managed to add some statis PNG files to it.
Now I'd like to load in a PNG sequence, which I believe is an AnimationDrawable (rather then Bitmap)
I have written the XML file for the animation but then I am stumped.
I cannot find any examples of people adding PNG sequences to a Canvas object.
here is sample:
public class MainActivity extends Activity {
private static final int FRAME_DELAY = 200; // in ms
private ArrayList<Bitmap> mBitmaps;
private final AtomicInteger mBitmapIndex = new AtomicInteger();
private View mView;
private Thread mThread;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// load resources
mBitmaps = new ArrayList<Bitmap>();
for(int resId : new int[]{
// resource ids here
R.drawable.ic_launcher,
R.drawable.ddms_128,
R.drawable.ddms_icon
}){
mBitmaps.add(BitmapFactory.decodeResource(getResources(), resId));
}
// create View and implement 'draw'
ViewGroup root = (ViewGroup) findViewById(R.id.root);
root.addView(mView = new View(this){
#Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mBitmaps.get(Math.abs(mBitmapIndex.get() % mBitmaps.size())), 10, 10, null);
super.draw(canvas);
}
});
}
#Override
protected void onStart() {
super.onStart();
mThread = new Thread(){
#Override
public void run() {
// wait and invalidate view until interrupted
while(true){
try {
Thread.sleep(FRAME_DELAY);
} catch (InterruptedException e) {
e.printStackTrace();
break; // get out if interrupted
}
mBitmapIndex.incrementAndGet();
mView.postInvalidate();
}
}
};
mThread.start();
}
#Override
protected void onStop() {
mThread.interrupt();
super.onStop();
}
}
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</RelativeLayout>
Canvas allows to draw line/shapes/bitmaps.
You managed to draw one bitmap.
Animation is just drawing one bitmap from bunch of them.
Canvas does nothing with drawables.
The logic neither related to Canvas nor to drawables.
Draw the first frame, then use a Handler to post delayed Message that when handled moves to the next frame and calls invalidate().
What you need to do is:
Step #1. Override VisualizerView.verifyDrawable(Drawable who)
#Override
protected boolean verifyDrawable(Drawable who) {
return true;
}
Step #2. Modify MainActivity.addAnimationRenderer()
private void addAnimationRenderer() {
final AnimationDrawable anim = (AnimationDrawable) getResources().getDrawable(R.drawable.png1);
AnimationRenderer animRenderer = new AnimationRenderer(anim);
mVisualizerView.addRenderer(animRenderer);
anim.setCallback(mVisualizerView);
mVisualizerView.post(new Runnable() {
#Override
public void run() {
anim.start();
}
});
}
Step #3. Modify AnimationRenderer: delete
mBitmap.setCallback(null);
mBitmap.start();
First of all, i don't know where, i don't know why, i get ConcurrentModificationException. My Logcat is out of order (or just i can't use it) but never shows any information about exceptions ( i read lots of article about it, and nothing helped, maybe can't debug my phone correctly )
Secondly sorry about my confused english, i try to formulate as clearly as i can, codes could help, please help me
So, The problem is the next:
i use Mapview and 5 custom CustomItemizedOverlay (Source no. 1) on it.
From MapView i start some (1, 2, max 5) threads to webservice (Source no. 2) and after i get results back (it's List) i draw them into mapoverlay (source no. 3)
so MapView ( implements 5 ResponseHandlerInterfaces ) sends requests to webservice through myActions (extends Thread) and when actions gets responses, they call responseHandler.reportResponseList(List list) methods. (MapView get back the control right here)
and all of it causes ConcurrentModificationException sometimes
(rarely ArrayIndexOutOfBoundsException)
i have got Options Activity to set required lists and i also have got Refresh button, to get lists. let me lead you through one example.
I just opened MapView, it's empty. I need only 1 kind of objects. I tap refresh, after network communication, i get markers on my mapview. cool, it's working. Now i'm going to Options, and i set more objects to request. Use Refresh again at mapview, and sometimes i get all kinds of objects, sometimes i get ConcurrentModificationException.
Source No. 1
public class CustomItemizedOverlay<T> extends ItemizedOverlay<T>{
private Context mContext;
private Object lock = new Object();
private CopyOnWriteArrayList<T> overlays = new CopyOnWriteArrayList<T>();
public CustomItemizedOverlay(Drawable marker, Context context) {
super(boundCenterBottom(marker));
this.mContext = context;
populate();
}
#Override
protected boolean onTap(int index){
// doesn't matter
}
public void clear(){
synchronized (lock) {
overlays.clear();
}
}
public void addOverlay(T overlay){
synchronized (lock) {
overlays.add(overlay);
setLastFocusedIndex(-1);
populate();
}
}
public void removeOverlay(int selected){
synchronized (lock) {
overlays.remove(selected);
populate();
setLastFocusedIndex(-1);
}
}
#Override
protected T createItem(int i) {
synchronized (lock) {
return overlays.get(i);
}
}
#Override
public int size() {
synchronized (lock) {
return overlays.size();
}
}
public void setLock(Object o){
this.lock = o;
}
}
Source No. 2
MapView:
public class MyMap extends MapActivity implements LocationListener, RestResPonseHandler { // there are 5 type of responsehandlers, one for each overlay
private MapView mapView;
private MyLocationOverlay myLocationOverlay;
private Object lock = new Object();
private CustomItemizedOverlay<CustomOverlayItem<MyObject1>> my1Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject2>> my2Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject3>> my3Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject4>> my4Overlay;
private CustomItemizedOverlay<CustomOverlayItem<MyObject5>> my5Overlay;
public void getObject1List(){ // there are 5 getList methods
new RestAction(this).start(); // 'this' is the object which implements required RestResponseHandler interface. in every case it will be 'this'. MyMap implements all kind of required RestResponseHandler interfaces
}
Source No. 3 (non main thread) // This is pattern for each 'CustomItemizedOverlay filling method'. After actions reports results (list of objects), mapview fills actual overlay with OverlayItems
#Override
public void reportResponseList(List<MyObject1> objects) {
if (my1Overlay == null){
List<Overlay> mapOverlays = mapView.getOverlays();
Drawable marker = this.getResources().getDrawable(R.drawable.icon);
my1Overlay = new CustomItemizedOverlay<CustomOverlayItem<MyObject1>>(marker, this);
my1Overlay.setLock(lock); // MyMap has lock object, look at source 2 (also CustomItemizedOverlay (source 1) )
mapOverlays.add(my1Overlay);
} else {
my1Overlay.clear();
}
synchronized (lock) {
for(int i=0;i<objects.size();++i){
MyObject1 object = objects.get(i);
CustomOverlayItem<MyObject1> item = new CustomOverlayItem<CustomBuilding>(object.getPositionId(), object);
my1Overlay.addOverlay(item);
}
refreshView();
}
}
Where refreshView posts runnable to main thread to update mapView.
public void refreshView(){
new Thread(new Runnable(){
#Override
public void run(){
mapView.post(new Runnable(){
#Override
public void run(){
mapView.invalidate();
}
});
}
}).start();
}
The Solution:
After CommonsWare's answer, i modified my source to :
#Override
public void reportResponseList(final List<MyObject1> objects) {
if (my1Overlay == null){
List<Overlay> mapOverlays = mapView.getOverlays();
Drawable marker = this.getResources().getDrawable(R.drawable.icon);
my1Overlay = new CustomItemizedOverlay<CustomOverlayItem<MyObject1>>(marker, this);
my1Overlay.setLock(lock);
mapOverlays.add(my1Overlay);
} else {
runOnUiThread(new Runnable(){
#Override
public void run(){
my1Overlay.clear();
}
});
}
runOnUiThread(new Runnable(){
#Override
public void run(){
for(int i=0;i<objects.size();++i){
MyObject1 object = objects.get(i);
CustomOverlayItem<MyObject1> item = new CustomOverlayItem<MyObject1>(object.getPositionId(), object);
my1Overlay.addOverlay(item);
}
refreshView();
}
});
}
and now at this moment it seems to work. i don't know how pretty is it, but seems to work. (maybe mapOverlays.add() method should be on main thread too) Thank you very much.
If my1Overlay is already part of the MapView by the time reportResponseList() is called, you should not be modifying it on a background thread. MapView will be using that Overlay. Instead, create a new Overlay in the background thread, the swap overlays (remove the old, add the new) on the main application thread.
I created a View component for android. The component has its own thread to make background work. I start the thread in the View constructor.
Where should I stop the thread? Is onDetachedFromWindow a correct place to make it?
I would do it the following way provided the Thread has to be active during the time the View has a Surface and uses it for drawing:
public class MyView extends View {
class MyThread extends Thread {
private boolean mActive = true;
public void run() {
while (mActive) {
doThings();
}
}
public void terminate() {
mActive = false;
}
private void doThings() {
// the things your thread should do
}
}
private MyThread mBackgroundOperation = null;
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mBackgroundOperation = new MyThread();
mBackgroundOperation.start();
}
protected void onDetachedFromWindow() {
mBackgroundOperation.terminate();
mBackgroundOperation = null;
super.onDetachedFromWindow();
}
}
If this is not the case (if the Thread's lifecycle is not directly dependant of the use of the View's Surface for drawing) I would think twice about handling it inside the View, and would maybe consider moving it to the Activity that uses this View instead. "Maybe" because this is a difficult thing to determine without knowing what this Thread actually does.
stop() method is deprecated. Check this:
http://developer.android.com/reference/java/lang/Thread.html#stop%28java.lang.Throwable%29
you should get your work done and leave it, Android is smart enough to take care of it..
The best time to stop a view updating is probably when it's no longer visible. Your activity's onStop() method will be called when this happens, and from there you could call a method you write in your custom view to shutdown its update thread.
// Activity.java
public void onStop() {
myThreadedView.shutdown();
... // rest of onStop() here
}
// ThreadedView.java
public void shutdown() {
myViewThread.shutdown();
}
// ViewThread.java
bool stop = false;
public void shutdown() {
stop = true;
}
public void run() {
while (!stop) {
updateView();
}
}
This question is about using the Google Android SDK, in the Java programming language.
My question could be boiled down to: Why is this code causing the android emulator to crash?
I've been wrestling for a few days with concurrency related to setting up different threads for a game app.
I have made many variations, but they have all failed. At this point, I just want to get a basic concurrent setup going. The worst part is that it is the emulator that crashes, so DDMS reports nothing; therefore I'm pretty clueless as to where the issue is.
The following code shows an activity (class Main), that calls class SceneManager, which creates a thread to be used for game logic stuff. A 3rd class, StatusChannel, is (will be) used to communicate status information between the different threads (Eventually, there will also be a OpenGL rendering thread).
The emulator crashes at different times. It may run for 20 seconds or for 5 minutes.
The setContentView(R.layout.main) in the Activity class just the set basic layout that Eclipse creates.
I've commented out the usage of Node (Created in the Activity class and accessed in SceneManager)
I have installed sdk versions 1.5 through 2.3 -- The current app is targeted at 2.1
The issue has something to do with the SceneManager class. I'm specially suspicious of the run() method.
Here are the 3 classes.
Sorry for the code length.
public class Main extends Activity {
private SceneManager mSceneManager;
private volatile Node mSceneGraph = new Node();
private volatile Status mStatusChannel = new Status();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d("-- Main", "onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Holds the scene assets, such as the stage,
// the agents, camera, etc.
mSceneManager = new SceneManager(mSceneGraph, mStatusChannel);
mSceneManager.onCreate();
}
#Override
protected void onResume() {
Log.d("-- Main", "onResume()");
super.onResume();
mSceneManager.onResume();
}
#Override
protected void onPause() {
Log.d("-- Main", "onPause()");
super.onPause();
mSceneManager.onPause();
}
#Override
protected void onDestroy() {
Log.d("-- Main", "onDestroy()");
super.onDestroy();
mSceneManager.onDestroy();
}
}
public class SceneManager implements Runnable{
private Thread mThread;
private volatile Status mStatusChannel;
private volatile Node mSceneGraph;
private volatile long mMillis = 0;
private volatile PrepareVisitor mPrepareVisitor;
private volatile int mStatus = Status.UNKNOWN_STATUS;
SceneManager(Node sceneGraph, Status statusChannel) {
mPrepareVisitor = new PrepareVisitor();
mStatusChannel = statusChannel;
mSceneGraph = sceneGraph;
mMillis = SystemClock.uptimeMillis();
mThread = new Thread(this);
mThread.setName("LogicThread");
mStatusChannel.setSceneManagerStatus(Status.READY_STATUS);
}
public void onCreate() {
Log.d("-- SceneManager", "onCreate()...");
// This will start the thread in a paused state.
mThread.start();
}
public void onResume() {
Log.d("-- SceneManager", "onResume()...");
// Unpause the status manager, if it is currently paused.
if (mStatusChannel.getSceneManagerStatus() == Status.PAUSED_STATUS) {
mStatusChannel.setSceneManagerStatus(Status.READY_STATUS);
}
}
public void onPause() {
Log.d("-- SceneManager", "onPause()...");
if (mStatusChannel.getSceneManagerStatus() != Status.UNKNOWN_STATUS) {
mStatusChannel.setSceneManagerStatus(Status.PAUSED_STATUS);
}
}
public void onDestroy() {
mStatusChannel.setSceneManagerStatus(Status.QUIT_STATUS);
try {
mThread.join();
}
catch (InterruptedException e) {
Log.d("-- SceneManager", "InterruptedException");
}
}
/**
* This method should not be called by clients of this class.
*/
#Override
public void run() {
Log.d("-- SceneManager", "Called...");
// Main logic loop.
outer: while (true) {
// How much time has elapsed since last call.
long timeDelta = SystemClock.uptimeMillis() - mMillis;
switch (mStatus) {
case Status.READY_STATUS:
//mPrepareVisitor.go(mSceneGraph, timeDelta);
break;
case Status.PAUSED_STATUS:
break;
case Status.QUIT_STATUS:
break outer;
case Status.UNKNOWN_STATUS:
int renderStatus = mStatusChannel.getRendererStatus();
if (renderStatus == Status.READY_STATUS) {
mStatusChannel.setSceneManagerStatus(Status.READY_STATUS);
}
break;
}
mStatus = mStatusChannel.getSceneManagerStatus();
// Update the time.
mMillis = SystemClock.uptimeMillis();
}
}
}
public class Status {
/* Generic Statuses */
public final static int UNKNOWN_STATUS = 0;
public final static int READY_STATUS = 1;
public final static int PAUSED_STATUS = 2;
public final static int QUIT_STATUS = 3;
/* Current statuses values */
private int mSceneManagerStatus = UNKNOWN_STATUS ;
private int mRendererStatus = UNKNOWN_STATUS ;
public synchronized int getSceneManagerStatus() {
return mSceneManagerStatus;
}
public synchronized int getRendererStatus() {
return mRendererStatus;
}
public synchronized void setSceneManagerStatus(int status) {
mSceneManagerStatus = status;
}
public synchronized void setRendererStatus(int status) {
mRendererStatus = status;
}
}
-- EDIT --
This issue happens even with something as simple as this:
public class ThreadActivity extends Activity {
private Booboo mBooboo;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBooboo = new Booboo();
mBooboo.onCreate();
}
}
public class Booboo implements Runnable {
private Thread mThread;
Booboo() {
mThread = new Thread(this, "SceneManagerThread");
}
public void onCreate() {
Log.d("Booboo", "Thread started");
mThread.start();
}
#Override
public void run() {
while (true) {}
}
}
I know the first reaction is to say that it's the while(true){}. Just remember that this is a contrived example to show the issue. In my own code, I do the lifecycle activity as described in the docs. The issue is that the emulator crashes after some time in an infinite loop like that, whether you have break conditions or not.
You probably want to look into AsyncTask. There is great article here : http://android-developers.blogspot.com/2009/05/painless-threading.html