I'm building a little game in HTML/JS on Android. I'm running into a problem on my HTC Desire (Android 2.2). When I touch the screen, all the images look pixelated and they get un-pixelated when the touch ends.
Here is a screenshot:
On the right it's when the screen is being touched. Can someone help me figure out what's causing this issue?
Notes:
No problems during the animations if the screen is not touched
I don't have this problem on my LG 540 Android 2.1
it seems images get a restricted number of colors when it's being touched
I'm using Phonegap
As far as I can tell, that "pixelated" behavior is an optimization made for scrolling (in Froyo and above). If the rendering is simplified, it makes things like the fling scroll animation require less processing.
If you need full browser functionality, I'm not sure you can help it much.
Since you've said you're making a game, however, I might have a workaround. I'm hoping your game doesn't need scroll (one full screen), so the scrolling optimization isn't necessary.
I've done a simple test with a WebView. On tap, as you mentioned, the rendering gets simplified, and things look a little off. Then once something is clicked (the WebView knows no more scrolling is taking place), things go back to normal.
I modified my Layout by replacing a WebView, with a FrameLayout. The FrameLayout contains the WebView and an invisible Button (on top). This Button grabs all the touch events. Then, I selectively choose what types of events the WebView should need, and pass them to the WebView. If a touch down and touch up happen close together, with no movement in betweeen, there's no reason for scrolling, so I haven't seen any of that "pixelated" behavior.
Because it was simplest for this example, I've chosen to detect the "MotionEvent.ACTION_UP" event, and when it's complete, I send a down first, so that it simulates a real click. You could certainly trigger on ACTION_DOWN, but you'll get more than one of those if the user swipes or something, and I wanted to keep the logic here simple. You can customize as you see fit, and probably with enough work, even enable scrolling in some cases. I hope the code below is enough to relay what I think works.
WebView wv = new WebView(this);
View dummyView = new Button(this);
dummyView.setBackgroundColor(0x00000000);
dummyView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
MotionEvent down = MotionEvent.obtain(100, 100,
MotionEvent.ACTION_DOWN, event.getX(),
event.getY(), 0);
wv.onTouchEvent(down);
wv.onTouchEvent(event);
}
return false;
}
});
FrameLayout fl = new FrameLayout(this);
fl.addView(wv);
fl.addView(dummyView);
topLayout.addView(fl);
EDIT:
If you don't want to edit PhoneGap source, you might be able to do something like the following to change the PhoneGap layout... It's untested, but seems like it should work:
#Override
public void onCreate(Bundle arg0) {
super.onCreate(arg0);
super.loadUrl("file:///android_asset/www/index.html");
// Get the "root" view from PhoneGap
LinearLayout droidGapRoot = super.root;
// Create a new "root" that we can use.
final LinearLayout newRoot = new LinearLayout(this);
for (int i = 0; i < droidGapRoot.getChildCount(); i++) {
// Move all views from phoneGap's LinearLayout to ours.
View moveMe = droidGapRoot.getChildAt(i);
droidGapRoot.removeView(moveMe);
newRoot.addView(moveMe);
}
// Create an invisible button to overlay all other views, and pass
// clicks through.
View dummyView = new Button(this);
dummyView.setBackgroundColor(0x00000000);
dummyView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Only pass "UP" events to the specific view we care about, but
// be sure to simulate a valid "DOWN" press first, so that the
// click makes sense.
if (event.getAction() == MotionEvent.ACTION_UP) {
MotionEvent down = MotionEvent.obtain(100, 100,
MotionEvent.ACTION_DOWN, event.getX(),
event.getY(), 0);
newRoot.onTouchEvent(down);
newRoot.onTouchEvent(event);
}
return false;
}
});
// Layer the views properly
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.addView(newRoot);
frameLayout.addView(dummyView);
// Add our new customized layout back to the PhoneGap "root" view.
droidGapRoot.addView(frameLayout);
}
I'm running into this same issue. One thing you could try is adding android:hardwareAccelerated="true" to your app's manifest in the tag. This stopped the problem for me, however now the whole app seems a little more pixelated overall on my device, so this might not be the best solution.
Check twice if your images have the same size in CSS in pixels as the files themselves. It seems to be somehow related. If I take a large image and re-scale it with a device-dependent generated CSS, I see the problem. Otherwise it's either not presented or not visible. Not sure if the original problem has been fixed in the latest Androids, but I still support 2.3, so hope it helps.
Related
I have developed a game that shoots when player touches the screen by using onTouchListener for my custom SurfaceView and Thread.
But now I want to change the approach and instead of onTouchListener I added onTouchEvent to the SurfaceView o my Activity.
The problem is that I get some kind of offset when I click on the emulator.
Everything is working great, except that offset keeps appearing and I don't understand why.
Also let me mention that my app is running in landscape mode, maybe this is relevant.
I suspect that it isn't working properly because onTouchListener was added to the view and depended on it, but onTouchEvent doesn't depend on the view.
Also my view doesn't have any padding or margin properties, it is full-screen view (fill_parent).
Does anyone have any ideas on this?
I have done my application, and everything works correctly now, but i still do not know what the problem was.
After lots of debugging of my application the onTouchEvent returned random Y values that were always higher than the ones that the onTouchListener returned. And i am not sure why this is hapening since my view that recognizes the onTouchListener is a full-screen view.
So i figured out a way to get passed this by doing some math.
The first function that the android calls is the onTouch method which gives the correct values but just for one touch. I needed it to give right values even on the MotionEvent.ACTION_MOVE so i noticed that MotionEvent.ACTION_MOVE is actually doing correctly depending on the first touch recognized by the onTouchEvent.
So i just got the coordinate Y from the onTouch and the different coordinate with the offset from onTouchEvent calculated the difference and in every onTouchEvent from there, until the user lifts up their finger, i just subtract that difference and that gives me the correct value.
If anyone else has this problem, and doesn't know how to fix it, here is my code, maybe it will be helpful.
#Override
public boolean onTouchEvent(MotionEvent arg1) {
/*you can only touch when the thread is running*/
if(game.state() != STATE_PAUSE){
if(arg1.getAction() == MotionEvent.ACTION_DOWN){
coordinateX = arg1.getX();
coordinateY = arg1.getY();
differenceY = Math.abs(coordinateY - touchedY);
coordinateY = coordinateY - differenceY;
shootingIsOkay = true;
game.setDrawCircle(coordinateX,coordinateY);
}
if(arg1.getAction() == MotionEvent.ACTION_MOVE){
coordinateX = arg1.getX();
coordinateY = arg1.getY();
coordinateY = coordinateY - differenceY;
shootingIsOkay = true;
game.setDrawCircle(coordinateX,coordinateY);
}
if(arg1.getAction() == MotionEvent.ACTION_UP){
shootingIsOkay = false;
}
}
return false;
}
And the onTouch method that is called from the onTouchListener that depends on the view is just simple, here:
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
touchedY = arg1.getY();
return false;
}
If anyone knows how to fix this problem, please post your answer, i am very curious why this is happening
I´m trying to add a ImageView (a symbol) each time the user touch on a Imageview (A car picture) in order to let the user indicate where the car has a damage. Then I send the points to a remote server so I can get the points when the acitivity is opened again. It works properly in the specific device that set the points, but if you get the points using another device whith different screen size and resolution, obviously, the symbols are not OK.
Is there any way to scale points and ImageView in order to make it possible? I mean, how could I make that, if you indicate there is a damage in the front/left door of the car, the symbol was always there when running the app in different screen sizes?
Code I´m using:
#Override
public boolean onTouch(View v, final MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
final RelativeLayout urLayoutId=(RelativeLayout) findViewById(R.id.RelativeCoche);
final ImageView imv=new ImageView(MainActivity.this);
imv.setImageResource(R.drawable.info);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(Math.round(event.getX()),
Math.round(event.getY()), 0, 0);
imv.setLayoutParams(params);
return true;
}
});
If I understand your question correctly, what you want to do is to include different images with the proper densities in the corresponding drawable-xxxx folder. Look at the last section of this link for more information.
I have a small button on a view that I cannot make larger (movers and shakers don't want it larger). It's very hard to click on the button because of other views around it. At androiddevcon III Dave someone or another gave a talk on expanding the hit area of a view but he didn't include the code in his example. Now I'm trying to figure out how to actually implement this.
What i'm trying to do is fire a Button.onClick event if a click occurs within a larger rect. The following code seems to work in the 2.3.4 emulator(hard to tell because the emulator doesn't have the functionality i'm trying to test) but doesn't at all on a Motorolla phone running android 2.3.4.
View view=findViewById(R.id.Header);
view.setOnTouchListener(new OnTouchListener(){
public boolean onTouch(View v, MotionEvent event) {
Rect rect=new Rect();
Button btn=(Button)findViewById(R.id.btn1);
btn.getHitRect(rect);
util.scaleRect(rect);
if (rect.contains((int)event.getX(), (int)event.getY())){
event.setLocation(rect.exactCenterX(), rect.exactCenterY());
btn.dispatchTouchEvent(event);
return true;
}
else{
return false;
}
}
});
Does anyone have any idea how this can be implemented?
Some examples:
https://play.google.com/store/apps/details?id=gpc.myweb.hinet.net.PopupVideo
https://play.google.com/store/apps/details?id=com.milone.floatwidget
Any idea? Thanks.
That is done using an overlay on top the existing activity regardless. You can find the source found here on my github to demonstrate the proof of concept that was on android's stackexchange.com question in which someone who suffered from a disability and could not swipe the screen to scroll and was looking for a way to do this conveniently, in which I cobbled together the code to show 'Page Up/Page Down' with minimum movement, just by tapping on the text (really, its a button) which overlaid on top of a activity. But due to the way Android and security worked, the scrolling event could not be injected into the currently running activity.
The way it works is this, from the onCreate activity there's this
WindowManager.LayoutParams layOutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
The flag that is needed is WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY and WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH.
The onus is to ensure that the touch is being handled which means watching out when the touch event 'hits' on a area and to act accordingly. By "listening" in on the View's onTouch event, that is:
LayoutInflater layOutInflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
View myView = layOutInflater.inflate(R.layout.myview, null);
myView.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
....
}
});
For example, imagine a button within that myview layout, so using a button widget,
Button myButton = (Button)myView.findViewById(R.id.myButton);
Rect outRectHit = new Rect();
myButton.getHitRect(outRectHit);
Now we need to determine the 'collision' of the boundaries of the touch, which happens inside the onTouch handler:
float x = event.getX();
float y = event.getY();
if (x > outRectHit.left && x < outRectHit.right &&
y > outRectHit.top && y < outRectHit.bottom){
... Handle this accordingly
}
This brief summary explains how to do such a thing as an overlay on-top of any activity.
Just to make sure, that the code gets debugged and does not interfere in any shape or form with any activity shown. For example, what happens if the activity is in fact a OpenGL game, what do you do?
That is to illustrate what needs to be watched out for.
You might want to have a look at Tooleap SDK.
It offers the simplest way of turning regular Android Activities into floating widgets.
E.g., let's say you have an Activity called MyActivity
Intent intent = new Intent(context, MyActivity.class);
TooleapPopOutMiniApp miniApp = new TooleapPopOutMiniApp(context, intent);
Tooleap tooleap = Tooleap.getInstance(context);
tooleap.addMiniApp(miniApp);
MyActivity will now become available as a floating widget on top of any other application.
I am using multiple animations and playing animations one after other. Currently using onAnimationEnd() to play animations one after other. In case of onTouch, I need to stop the animation and need to set different bitmap to imageview in touch location. Currently using below code but facing problems in case of clearAnimation().
#Override
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
imageArray[g_animCount - 1].clearAnimation();
break;
default:
break;
}
return true; // indicate event was handled
}
#Override
public void onAnimationEnd(Animation animation) {
layout.removeView(imageArray[g_animCount - 1]);
if ( (g_animCount < count))
{
startNextAnimation();
}
else
{
g_animCount = 0;
isBegin = false;
}
}
Problems and queries:
After clear animation, I could see image again at the beginning location, how to keep it at touch location ? tried setFillAfter(true) but no use.
How to define onAnimationEnd() in case to play animations one after other ? do we need to remove imageview?
Without clearAnimation() I do not have any problems but it does not solve my problem. Kindly correct my code.
1) to keep the ImageView to the new location you have to specify the new coordinates.
You can do it in this way:
MarginLayoutParams l = new MarginLayoutParams(v.getLayoutParams());
l.leftMargin = left;
l.topMargin = top;
v.setLayoutParams(l);
v is your ImageView, left and top are the coordinates (x,y) to place the view on the screen. The parent should be a RelativeLayout.
2) it's better to start all the animation in sequence avoid calling new start in the callback methods (onAnimationEnd, etc). You don't need to remove the view if you have to use it later in the flow
My reply may be a bit out of date, but better later than never.
I also noticed this nasty bug: when you call cancelAnimation, the AnimationListener.onAnimationEnd is called, however the view stays and there is no "legal" ways to hide it (tried remove view from layout, set visibility to View.INVISIBLE, even negative marginTop and marginBottom - nothing works). The bug is present with Andoid 2.3 and Android 4.0. It doesn't turn up with KitKat, presumably has been fixed.
Here's the good news. I've found a work around with ImageView: in AnimationListener.onAnimationEnd I say animationView.setImageDrawable(null) and the view disappears. Hopefully this will help someone.