I'm developing simple Android game, but I'm running into problems while testing it. When I run it on Lenovo Tab3 7 tablet (Android 5.0.1 ) or LG P880 phone (Android 4.0.3) it works fine. When I run it on Samsung S7 phone (Android 7.0) game usually runs fine. What I mean by this is that I can run it 10 times in a row with no problems, but sometimes game halts for 5-30 seconds or stops responding. This usually happens during starting of new Activity or very shortly after it.
Game has 4 Activities which use extended SurfaceView as layout. All SurfaceViews implement Runnable. Activities are: Splash screen (noHistory = "true" in Manifest), Menu, Difficulty choice and Game.
I use only mdpi drawables and scale them proportionally to all screen sizes. Bitmaps are loaded using BitmapFactory.decodeResource with BitmapFactory.Options inDensity = 1, inScaled = false.
When the problem occurs logcat shows only garbage collection. Sometimes game "pauses" (no taps are registered) for 5-30 seconds and resumes normally, sometimes it has to be restarted due to no response. I feel like game stops collecting input for some reason. Input is handled by overriding onTouchEvent and checking if ACTION_UP is within tapped image bounds. As I said, this happens only on S7 (I tried it on two phones), not on tablet or P880, so I'm thinking it might be something to do with Nougat or me forcing lower density on the phone.
So, since I'm running out of ideas what could be causing this and me being new to Android game development, does anyone know/have any idea where I should be looking for solution? Is there anything Nougat-specific I should be setting/checking? Does forcing pixel density affect device performance in any way?
Edit 1
globalApp
public class globalApp extends Application {
SoundPool soundPool;
SoundPool.Builder soundPoolBuilder;
AudioAttributes audioAttributes;
AudioAttributes.Builder audioAttributesBuilder;
int soundTap, soundCorrect, soundIncorrect, soundVictory, soundDefeat;
int soundBarrelVerySlow, soundBarrelSlow, soundBarrelNormal, soundBarrelFast, soundBarrelVeryFast;
#Override
public void onCreate() {
super.onCreate();
}
public void buildSoundPool(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
audioAttributesBuilder = new AudioAttributes.Builder();
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_GAME);
audioAttributesBuilder.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION);
audioAttributes = audioAttributesBuilder.build();
soundPoolBuilder = new SoundPool.Builder();
soundPoolBuilder.setMaxStreams(2);
soundPoolBuilder.setAudioAttributes(audioAttributes);
soundPool = soundPoolBuilder.build();
}
else {
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
}
}
public void loadSounds(){
soundBarrelVerySlow = soundPool.load(this,R.raw.very_slow_move, 1);
soundBarrelSlow = soundPool.load(this, R.raw.slow_move, 1);
soundBarrelNormal = soundPool.load(this, R.raw.slow_move, 1);
soundBarrelFast = soundPool.load(this,R.raw.fast_move, 1);
soundBarrelVeryFast = soundPool.load(this,R.raw.very_fast_move, 1);
soundTap = soundPool.load(this, R.raw.tap_sound, 1);
soundCorrect = soundPool.load(this, R.raw.correct, 1);
soundIncorrect = soundPool.load(this, R.raw.incorrect, 1);
soundVictory = soundPool.load(this, R.raw.victory, 1);
soundDefeat = soundPool.load(this, R.raw.defeat, 1);
}
public void playTap(){
soundPool.play(soundTap, 1, 1,1, 0, 1);
}
public void playCorrect(){
soundPool.play(soundCorrect, 1, 1,1, 0, 1);
}
public void playIncorrect(){
soundPool.play(soundIncorrect, 1, 1,1, 0, 1);
}
public void playVictory(){
soundPool.play(soundVictory, 1, 1,1, 0, 1);
}
public void playDefeat(){
soundPool.play(soundDefeat, 1, 1,1, 0, 1);
}
public void playBarrelVerySlow(){soundPool.play(soundBarrelVerySlow, 1, 1, 1, 0, 1);}
public void playBarrelSlow(){soundPool.play(soundBarrelSlow, 1, 1, 1, 0, 1);}
public void playBarrelNormal(){
soundPool.play(soundBarrelNormal, 1, 1,1, 0, 1);
}
public void playBarrelFast(){soundPool.play(soundBarrelFast, 1, 1, 1, 0, 1);}
public void playBarrelVeryFast(){soundPool.play(soundBarrelVeryFast, 1, 1, 1, 0, 1);}
}
MenuItem
public class MenuItem {
private Bitmap bmp;
private Context context;
private Rect sourceRect;
private RectF destRect;
private int srcWidth;
private int srcHeight;
private int destW, destH;
private int x, y;
private int screenH;
public MenuItem(Context ctx, String bmpName, int w, int x, int y, int sX, int sY){
context = ctx;
BitmapFactory.Options bmpFOptions = new BitmapFactory.Options();
bmpFOptions.inDensity = 1;
bmpFOptions.inScaled = false;
int res = context.getResources().getIdentifier(bmpName, "drawable", ctx.getPackageName());
bmp = BitmapFactory.decodeResource(ctx.getResources(), res, bmpFOptions);
srcWidth = w;
srcHeight = bmp.getHeight();
this.x = x;
this.y = y;
screenH = sY;
sourceRect = new Rect(0,0, srcWidth, srcHeight);
destRect = new RectF();
setProportionalDestinationRect(sX, sY);
}
private void setProportionalDestinationRect(int scrX, int scrY) {
if (scrX != 1024 || scrY != 552){
float propX = (float)scrX/1024;
float propY = (float)scrY/600;
// All drawables are designed for 1024x600 screen
// if device screen is different, scale image proportionally
destW = (int)(srcWidth * propX);
destH = (int) (srcHeight * propY);
x = (int) (x*propX);
y = (int) (y*propY);
}
else {
destW = srcWidth;
destH = srcHeight;
}
destRect.set(x,y, x+destW,y+destH);
}
public void update(){
}
public Bitmap getBmp() {
return bmp;
}
public void setBmp(Bitmap bmp) {
this.bmp = bmp;
}
public Rect getSourceRect() {
return sourceRect;
}
public void setSourceRect(Rect sourceRect) {
this.sourceRect = sourceRect;
}
public RectF getDestRect() {
return destRect;
}
public void setDestRect(RectF destRect) {
this.destRect = destRect;
}
public boolean contains(int x, int y){
if (destRect.left <= x && destRect.right >= x)
if (destRect.top <= y && destRect.bottom >= y)
return true;
return false;
}
public void setY(int y) {
this.y = y;
if (screenH != 552){
float propY = (float)screenH/600;
y = (int) (y*propY);
}
destRect.set(x,y, x+destW,y+destH);
}
}
MainActivity
public class MainActivity extends Activity {
private boolean backPressedOnce = false;
long backPressedTime = 0;
private MainActivitySurface mainActivitySurface;
globalApp app;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
int x = getIntent().getIntExtra("screenWidth", 500);
int y = getIntent().getIntExtra("screenHeight", 500);
app = (globalApp) getApplication();
app.buildSoundPool();
app.loadSounds();
mainActivitySurface = new MainActivitySurface(this, app, x, y);
mainActivitySurface.setParentActivity(MainActivity.this);
setContentView(mainActivitySurface);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
if (resultCode == RESULT_OK) {
int result = data.getIntExtra("difficulty", 3);
mainActivitySurface.setResultDifficulty(result);
}
}
}
#Override
protected void onPause() {
super.onPause();
mainActivitySurface.pause();
}
#Override
protected void onResume() {
super.onResume();
backPressedOnce = false;
mainActivitySurface.resume();
}
#Override
public void onBackPressed() {
if (backPressedOnce && backPressedTime + 2000 > System.currentTimeMillis()) {
Process.killProcess(Process.myPid());
System.exit(1);
} else {
Toast.makeText(this, "Press back again to exit.", Toast.LENGTH_SHORT).show();
backPressedOnce = true;
}
backPressedTime = System.currentTimeMillis();
}
}
MainActivitySurface
public class MainActivitySurface extends SurfaceView implements Runnable {
private Context context;
private SurfaceHolder surfaceHolder;
private Canvas canvas;
private Thread thread = null;
volatile private boolean running = false;
private boolean surfaceCreated = false;
private Intent playIntent;
private Intent difficultyIntent;
// Screen size
private int screenWidth, screenHeight;
//Menu items
private MenuItem menuItemPlay, menuItemDifficulty, middleBarrel, bg;
private int difficulty = 3;
private Activity parentActivity;
private globalApp app;
public MainActivitySurface(Context ctx, globalApp a, int scrW, int scrH){
super(ctx);
context = ctx;
screenHeight = scrH;
screenWidth = scrW;
app = a;
surfaceHolder = getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceCreated = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
bg = new MenuItem(context, "main_activity_background_single", 1024, 0, 0, scrW, scrH);
menuItemPlay = new MenuItem(context, "menu_item_play_single", 233,(1024-233)/2,100, scrW, scrH);
menuItemDifficulty = new MenuItem(ctx, "menu_item_difficulty_single", 520,(1024 - 520)/2,400,scrW,scrH);
middleBarrel = new MenuItem(ctx, "middle_barrel_single", 323,(1024-323)/2,200,scrW,scrH);
playIntent = new Intent(context, GameActivity.class);
playIntent.putExtra("screenWidth", screenWidth);
playIntent.putExtra("screenHeight", screenHeight);
}
#Override
public void run() {
while (running){
draw();
}
}
private void draw() {
if(surfaceHolder.getSurface().isValid()){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bg.getBmp(), bg.getSourceRect(), bg.getDestRect(), null);
canvas.drawBitmap(menuItemPlay.getBmp(), menuItemPlay.getSourceRect(), menuItemPlay.getDestRect(), null);
canvas.drawBitmap(menuItemDifficulty.getBmp(), menuItemDifficulty.getSourceRect(), menuItemDifficulty.getDestRect(), null);
canvas.drawBitmap(middleBarrel.getBmp(), middleBarrel.getSourceRect(), middleBarrel.getDestRect(), null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void resume(){
running = true;
thread = new Thread(this);
thread.start();
}
public void pause(){
running = false;
boolean retry = false;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
Log.d("info", "MainActivitySurface: Error joining thread");
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & event.ACTION_MASK){
case MotionEvent.ACTION_UP:
if (menuItemPlay.contains((int) event.getX(), (int) event.getY())){
app.playTap();
parentActivity.startActivity(playIntent);
parentActivity.overridePendingTransition(0,0);
break;
}
if (menuItemDifficulty.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficultyIntent = new Intent(parentActivity, DifficultyActivity.class);
difficultyIntent.putExtra("screenWidth", screenWidth);
difficultyIntent.putExtra("screenHeight", screenHeight);
difficultyIntent.putExtra("difficulty", difficulty);
parentActivity.startActivityForResult(difficultyIntent, 1001);
parentActivity.overridePendingTransition(0, 0);
break;
}
}
return true;
}
public void setParentActivity(Activity act){
parentActivity = act;
}
public void setResultDifficulty(int diff){
difficulty = diff;
playIntent.putExtra("difficulty", difficulty);
}
}
DifficultyActivity
public class DifficultyActivity extends Activity {
private DifficultySurface surface;
private globalApp app;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
app = (globalApp) getApplication();
surface = new DifficultySurface(this, app, getIntent().getIntExtra("screenWidth", 500), getIntent().getIntExtra("screenHeight", 500));
setContentView(surface);
}
#Override
protected void onPause() {
super.onPause();
app.soundPool.release();
surface.pause();
overridePendingTransition(0, 0);
}
#Override
protected void onResume() {
super.onResume();
app.buildSoundPool();
app.loadSounds();
surface.resume();
}
}
DifficultySurface
public class DifficultySurface extends SurfaceView implements Runnable {
private SurfaceHolder surfaceHolder;
private Thread thread = null;
private Canvas canvas;
private Context context;
private globalApp app;
private boolean surfaceCreated = false;
private boolean running = false;
private MenuItem bgProp, arrowBarrel, okButton, diffVeryEasy, diffEasy, diffNormal, diffHard, diffVeryHard;
private int difficulty;
public DifficultySurface(Context ctx, globalApp a, int scrW, int scrH){
super(ctx);
context = ctx;
app = a;
surfaceHolder = getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceCreated = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
difficulty = ((Activity)context).getIntent().getIntExtra("difficulty", 3);
bgProp = new MenuItem(ctx, "difficulty_background", 1024, 0, 0, scrW, scrH);
diffVeryEasy = new MenuItem(ctx, "very_easy",796, 100, 100, scrW, scrH);
diffEasy = new MenuItem(ctx, "easy",796, 100, 200 , scrW, scrH);
diffNormal = new MenuItem(ctx, "normal",796, 100, 300, scrW, scrH);
diffHard = new MenuItem(ctx, "hard",796, 100, 400 , scrW, scrH);
diffVeryHard = new MenuItem(ctx, "very_hard",796, 100, 500, scrW, scrH);
okButton = new MenuItem(ctx, "ok_button", 100, 924, 500, scrW, scrH);
arrowBarrel = new MenuItem(ctx, "barrel_arrow", 100, 0, 100*difficulty, scrW, scrH);
}
#Override
public void run() {
while (running) {
if (surfaceCreated) {
update();
draw();
}
}
}
private void update() {
arrowBarrel.setY(difficulty*100);
}
private void draw() {
if (surfaceHolder.getSurface().isValid()){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bgProp.getBmp(), bgProp.getSourceRect(), bgProp.getDestRect(), null);
canvas.drawBitmap(arrowBarrel.getBmp(), arrowBarrel.getSourceRect(), arrowBarrel.getDestRect(), null);
canvas.drawBitmap(diffVeryEasy.getBmp(), diffVeryEasy.getSourceRect(), diffVeryEasy.getDestRect(), null);
canvas.drawBitmap(diffEasy.getBmp(), diffEasy.getSourceRect(), diffEasy.getDestRect(), null);
canvas.drawBitmap(diffNormal.getBmp(), diffNormal.getSourceRect(), diffNormal.getDestRect(), null);
canvas.drawBitmap(diffHard.getBmp(), diffHard.getSourceRect(), diffHard.getDestRect(), null);
canvas.drawBitmap(diffVeryHard.getBmp(), diffVeryHard.getSourceRect(), diffVeryHard.getDestRect(), null);
canvas.drawBitmap(okButton.getBmp(), okButton.getSourceRect(), okButton.getDestRect(), null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & event.ACTION_MASK){
case MotionEvent.ACTION_UP:{
if (diffVeryEasy.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 1; }
if (diffEasy.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 2;
}
if (diffNormal.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 3;
}
if (diffHard.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 4;
}
if (diffVeryHard.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 5;
}
if (okButton.contains((int)event.getX(), (int) event.getY())){
app.playTap();
((Activity)context).getIntent().putExtra("difficulty", difficulty);
((Activity)context).setResult(Activity.RESULT_OK, ((Activity)context).getIntent());
((Activity)context).finish();
((Activity)context).overridePendingTransition(0, 0);
}
break;
}
}
return true;
}
public void pause(){
running = false;
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
((Activity)context).overridePendingTransition(0, 0);
}
public void resume(){
running = true;
thread = new Thread(this);
thread.start();
}
}
GameActivity
public class GameActivity extends Activity {
private GameSurface surface;
private globalApp app;
private int difficulty;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
difficulty = getIntent().getIntExtra("difficulty", 3);
app = (globalApp) getApplication();
surface = new GameSurface(this, app, getIntent().getIntExtra("screenWidth", 500), getIntent().getIntExtra("screenHeight", 500), difficulty);
surface.setParentActivity(this);
setContentView(surface);
}
#Override
protected void onPause() {
super.onPause();
app.soundPool.release();
surface.pause();
}
#Override
protected void onPostResume() {
super.onPostResume();
app.buildSoundPool();
app.loadSounds();
surface.resume();
}
#Override
protected void onStop() {
super.onStop();
surface.stop();
}
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
}
Game halting happens either when I start DificultyActivity (I tap one MenuItem objects but nothing happens) or when I start GameActivity (game still shows MainActivity + MainActivitySurface).
Android Monitor show less than 40MB of allocated memory, so bitmaps shouldn't be the problem in my opinion. I tried recycling all bitmaps but the problem was present (that's why I opted to use only mdpi drawables; at first I used all pixel densities but tried lowering resources in case that was causing halts).
It is hard to find the problem without looking at the code. There's nothing nougat-specific way of handling the resources.
But android N claims to have a better memory management and since you are complaining a lot of garbage collections, it may be one of the cause. Make sure to recycle the unused bitmaps. And use RGB_565 as the preferred bitmap config which requires half memory than RGB_8888.
I have solved my problem. After posting question I came across this. It seems we had the same problem. When I slowed down drawing speed (using thread.sleep) there were no more issues.
Thanks to those who helped me.
I want to to add the media controller on VLC player android integration but i didn't find any option to do that i am sharing the code -
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.otherformat);
load = (ProgressBar) this.findViewById(R.id.load);
intent = getIntent();
load.setVisibility(View.VISIBLE);
handler = new Handler();
mFilePath ="http://mm2.pcslab.com/mm/7h800.mp4";
Log.e(TAG, "Playing: " + mFilePath);
mSurface = (SurfaceView) findViewById(R.id.surface);
holder = mSurface.getHolder();
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setSize(mVideoWidth, mVideoHeight);
}
#Override
protected void onResume() {
super.onResume();
createPlayer(mFilePath);
}
#Override
protected void onPause() {
super.onPause();
releasePlayer();
}
#Override
protected void onDestroy() {
super.onDestroy();
releasePlayer();
}
/**
* Used to set size for SurfaceView
*
* #param width
* #param height
*/
private void setSize(int width, int height) {
mVideoWidth = width;
mVideoHeight = height;
if (mVideoWidth * mVideoHeight <= 1)
return;
if (holder == null || mSurface == null)
return;
int w = getWindow().getDecorView().getWidth();
int h = getWindow().getDecorView().getHeight();
boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
if (w > h && isPortrait || w < h && !isPortrait) {
int i = w;
w = h;
h = i;
}
float videoAR = (float) mVideoWidth / (float) mVideoHeight;
float screenAR = (float) w / (float) h;
if (screenAR < videoAR)
h = (int) (w / videoAR);
else
w = (int) (h * videoAR);
holder.setFixedSize(mVideoWidth, mVideoHeight);
ViewGroup.LayoutParams lp = mSurface.getLayoutParams();
lp.width = w;
lp.height = h;
mSurface.setLayoutParams(lp);
mSurface.invalidate();
}
/**
* Creates MediaPlayer and plays video
*
* #param media
*/
private void createPlayer(String media) {
releasePlayer();
try {
if (media.length() > 0) {
Toast toast = Toast.makeText(this, media, Toast.LENGTH_LONG);
toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0,
0);
//toast.show();
}
// Create LibVLC
// TODO: make this more robust, and sync with audio demo
ArrayList<String> options = new ArrayList<String>();
//options.add("--subsdec-encoding <encoding>");
options.add("--aout=opensles");
options.add("--audio-time-stretch"); // time stretching
options.add("-vvv"); // verbosity
libvlc = new LibVLC(this, options);
holder.setKeepScreenOn(true);
// Creating media player
mMediaPlayer = new MediaPlayer(libvlc);
mMediaPlayer.setEventListener(mPlayerListener);
mMediaPlayer.setVideoTrackEnabled(true);
// Seting up video output
final IVLCVout vout = mMediaPlayer.getVLCVout();
vout.setVideoView(mSurface);
//vout.setSubtitlesView(mSurfaceSubtitles);
vout.addCallback(this);
vout.attachViews();
Media m = new Media(libvlc, Uri.parse(media));
mMediaPlayer.setMedia(m);
mMediaPlayer.play();
////////////////////////////////////////////////
/* //mController = new MediaController(this);
mController = (MediaController)findViewById(R.id.mediaController);
mController.setVisibility(View.VISIBLE);
mController.setAnchorView(mSurface);
mController.show(0);*/
/*mController = new MediaController(this);
mController.setAnchorView(mSurface);
handler.post(new Runnable() {
public void run() {
mController.show();
}
});*/
} catch (Exception e) {
Log.e("Err", e.getMessage()+"");
Toast.makeText(this, "Error in creating player!"+e.getMessage(), Toast
.LENGTH_LONG).show();
}
}
private void releasePlayer() {
if (libvlc == null)
return;
mMediaPlayer.stop();
final IVLCVout vout = mMediaPlayer.getVLCVout();
vout.removeCallback(this);
vout.detachViews();
holder = null;
libvlc.release();
libvlc = null;
mVideoWidth = 0;
mVideoHeight = 0;
}
/**
* Registering callbacks
*/
private MediaPlayer.EventListener mPlayerListener = new MyPlayerListener(this);
#Override
public void onNewLayout(IVLCVout vout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen) {
if (width * height == 0)
return;
// store video size
mVideoWidth = width;
mVideoHeight = height;
setSize(mVideoWidth, mVideoHeight);
}
#Override
public void onSurfacesCreated(IVLCVout vout) {
}
#Override
public void onSurfacesDestroyed(IVLCVout vout) {
}
#Override
public void onHardwareAccelerationError(IVLCVout vlcVout) {
Log.e(TAG, "Error with hardware acceleration");
this.releasePlayer();
Toast.makeText(this, "Error with hardware acceleration", Toast.LENGTH_LONG).show();
}
private static class MyPlayerListener implements MediaPlayer.EventListener {
private WeakReference<OtherFormat> mOwner;
public MyPlayerListener(OtherFormat owner) {
mOwner = new WeakReference<OtherFormat>(owner);
}
#Override
public void onEvent(MediaPlayer.Event event) {
OtherFormat player = mOwner.get();
switch (event.type) {
case MediaPlayer.Event.EndReached:
Log.d(TAG, "MediaPlayerEndReached");
player.releasePlayer();
break;
case MediaPlayer.Event.Playing:
load.setVisibility(View.GONE);
break;
case MediaPlayer.Event.Paused:
case MediaPlayer.Event.Buffering:
float percent = event.getBuffering(); /* from 0.0f to 100f */
Log.e("Percent><><",""+percent);
break;
case MediaPlayer.Event.Stopped:
default:
break;
}
}
}}
XML for this activity -
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<SurfaceView
android:id="#+id/surface"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center" />
<ProgressBar
android:id="#+id/load"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:visibility="visible"
android:layout_gravity="center"
/>
<MediaController
android:id="#+id/mediaController"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" >
</MediaController>
</FrameLayout>
By this code mediacontrollers not showing please help me how to show media controllers for pause, play and seekbar for video position in surfaceview of vlc media player.
I had the same issue and have found a solution.
Sure, it's too late for answer, but maybe I will save some time to someone.
To add default media control you need to implement it in following way
remove MediaController from layout and add some container for media control to to your activity (it will be added programmatically).
<FrameLayout
android:id="#+id/video_surface_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:foregroundGravity="clip_horizontal|clip_vertical"
tools:ignore="true">
<SurfaceView
android:id="#+id/surface"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center" />
</FrameLayout>
add controller init to onStart activity or to onCreate
import android.widget.MediaController;
private MediaController controller;
#Override
protected void onStart() {
super.onStart();
....
controller = new MediaController(this);
controller.setMediaPlayer(findViewByid(R.id.video_surface_frame));
controller.setAnchorView(mVideoSurfaceFrame);
mVideoSurfaceFrame.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
controller.show(10000);
}
});
}
to define playerInterface, you need to implement the MediaController.MediaPlayerControl interface like this
private MediaController.MediaPlayerControl playerInterface = new MediaController.MediaPlayerControl() {
public int getBufferPercentage() {
return 0;
}
public int getCurrentPosition() {
float pos = mLibVlc.getPosition();
return (int)(pos * getDuration());
}
public int getDuration() {
return (int)mLibVlc.getLength();
}
public boolean isPlaying() {
return mLibVlc.isPlaying();
}
public void pause() {
mLibVlc.pause();
}
public void seekTo(int pos) {
mLibVlc.setPosition((float)pos / getDuration());
}
public void start() {
mLibVlc.play();
}
public boolean canPause() {
return true;
}
public boolean canSeekBackward() {
return true;
}
public boolean canSeekForward() {
return true;
}
#Override
public int getAudioSessionId() {
return 0;
}
};
launch your app and tap on video. You will see default media control and it will hide in 10 sec
Good luck!
Link to VideoLan sample on github:
https://code.videolan.org/videolan/vlc-android/blob/f3db2e4a33bf96c03056874322fb5b9304f92259/vlc-android/src/vlc/android/VLC.java
public class Show_Array extends AppCompatActivity implements IVLCVout.Callback {
private TextView container_extension;
private String stream_typee,stream_idd,container_extensionn ;
private String SAMPLE_URL = "";
public int mHeight;
public int mWidth;
private SurfaceView mVideoSurface = null;
private FrameLayout sdk;
private IVLCVout vlcVout;
private LibVLC mLibVlc = null;
private MediaPlayer mMediaPlayer = null;
private int flag = 0;
private ImageButton Resize;
private Media media;
private ArrayList<String> args;
private SurfaceHolder mSurfaceHolderVideo;
private MediaController controller;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.show__array);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
//Referances
Resize = findViewById(R.id.Resize);
mVideoSurface = findViewById(R.id.video_surface);
stream_typee = getIntent().getExtras().getString("stream_type");
stream_idd = getIntent().getExtras().getString("stream_id");
container_extensionn = getIntent().getExtras().getString("container_extension");
args = new ArrayList<>();
args.add("-vvv");
mLibVlc = new LibVLC(this, args);
mMediaPlayer = new MediaPlayer(mLibVlc);
vlcVout = mMediaPlayer.getVLCVout();
sdk = findViewById(R.id.sdk);
Resize_video();
setup_url();
controller = new MediaController(this);
controller.setMediaPlayer(playerInterface);
controller.setAnchorView(mVideoSurface);
mVideoSurface.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
controller.show(10000);
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
mMediaPlayer.release();
mLibVlc.release();
}
void Resize_video()
{
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
mHeight = displayMetrics.heightPixels;
mWidth = displayMetrics.widthPixels;
Resize.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
flag+=1;
int width = 1000;
int height = 560;
if(flag%2!=0) {
LinearLayout.LayoutParams myImageLayout = new LinearLayout.LayoutParams(width, height);
myImageLayout.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;
sdk.setLayoutParams(myImageLayout);
vlcVout.setWindowSize(width,height);
}
else
{
LinearLayout.LayoutParams myImageLayout = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
myImageLayout.gravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL;
sdk.setLayoutParams(myImageLayout);
vlcVout.setWindowSize(mWidth,mHeight);
}
}
});
}
void setup_url()
{
//TextView
container_extension = findViewById(R.id.URL);
if (stream_typee.equals("live"))
{
SAMPLE_URL = "http://uautv.ru:2095/"+stream_typee+"/webserver/6tE#BzW73#sX/"+stream_idd+".ts";
container_extension.setText( SAMPLE_URL);
}else
{
SAMPLE_URL = "http://uautv.ru:2095/"+stream_typee+"/webserver/6tE#BzW73#sX/"+stream_idd+"."+container_extensionn;
container_extension.setText( SAMPLE_URL);
}
}
#Override
protected void onStart() {
super.onStart();
vlcVout.setWindowSize(mWidth,mHeight);
vlcVout.setVideoView(mVideoSurface);
vlcVout.attachViews();
mMediaPlayer.getVLCVout().addCallback(this);
if(!SAMPLE_URL.isEmpty()) {
media = new Media(mLibVlc, Uri.parse(SAMPLE_URL));
mMediaPlayer.setMedia(media);
media.release();
mMediaPlayer.play();
}else
{
Toast.makeText(getApplicationContext(),"URL EMPTY",Toast.LENGTH_LONG).show();
}
}
#Override
protected void onStop() {
super.onStop();
mMediaPlayer.stop();
mMediaPlayer.getVLCVout().detachViews();
mMediaPlayer.getVLCVout().removeCallback(this);
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
#Override
public void onNewLayout(IVLCVout vlcVout, int width, int height, int visibleWidth, int visibleHeight, int sarNum, int sarDen)
{ }
#Override
public void onSurfacesCreated(IVLCVout vlcVout) {
}
#Override
public void onSurfacesDestroyed(IVLCVout vlcVout) {
}
private MediaController.MediaPlayerControl playerInterface = new MediaController.MediaPlayerControl() {
public int getBufferPercentage() {
return 0;
}
public int getCurrentPosition() {
float pos = mMediaPlayer.getPosition();
return (int)(pos * getDuration());
}
public int getDuration() {
return (int)mMediaPlayer.getLength();
}
public boolean isPlaying() {
return mMediaPlayer.isPlaying();
}
public void pause() {
mMediaPlayer.pause();
}
public void seekTo(int pos) {
mMediaPlayer.setPosition((float)pos / getDuration());
}
public void start() {
mMediaPlayer.play();
}
public boolean canPause() {
return true;
}
public boolean canSeekBackward() {
return true;
}
public boolean canSeekForward() {
return true;
}
#Override
public int getAudioSessionId() {
return 0;
}
};
}
I am new to Android development and currently I am implementing Live wallpaper application.So, My question is How I can change live wallpaper when new live wallpaper is selected from the application.currently I am setting only one live wallpaper from my application but issue is that when I am selecting wallpaper from my application to set as wallpaper it is not change and display previously selected wallpaper.And when i am restarting my device then it will display.I am using Glide library to display Gif image.
Here this my WallpaperService class
public class GifPaperService extends WallpaperService {
static final String TAG = "gifService";
static final Handler gifHandler = new Handler();
int position;
boolean visible;
ImageAdapter img = new ImageAdapter();
Integer[] mThumb = img.mThumbIds;
#Override
public void onCreate() {
super.onCreate();
Log.v("Helllo", "...");
}
#Override
public Engine onCreateEngine() {
try {
return new GifEngine();
} catch (IOException e) {
Log.w(TAG, "Error creating engine", e);
stopSelf();
return null;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("Hello..", ".....");
return super.onStartCommand(intent, flags, startId);
}
class GifEngine extends Engine {
private final Movie gif;
private final int duration;
private final Runnable runnable;
float scaleX;
float scaleY;
int when;
long start;
GifEngine() throws IOException {
MyPreferenceActivity myPref = new MyPreferenceActivity(getApplicationContext());
Log.i("Imageis... ", "Position.." + myPref.getGifImage());
position = myPref.getGifImage();
InputStream is = getResources().openRawResource(mThumb[position]);
Log.i("Imageposition...", "...." + mThumb[position]);
if (is == null) {
throw new IOException("Unable to open whoa.gif");
}
try {
gif = Movie.decodeStream(is);
duration = gif.duration();
} finally {
is.close();
}
when = -1;
runnable = new Runnable() {
#Override
public void run() {
animateGif();
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
gifHandler.removeCallbacks(runnable);
}
#Override
public void onVisibilityChanged(boolean visible) {
super.onVisibilityChanged(visible);
if (visible) {
animateGif();
} else {
gifHandler.removeCallbacks(runnable);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
scaleX = width / (1f * gif.width());
scaleY = height / (1f * gif.height());
animateGif();
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep,
int xPixelOffset, int yPixelOffset) {
super.onOffsetsChanged(
xOffset, yOffset,
xOffsetStep, yOffsetStep,
xPixelOffset, yPixelOffset);
animateGif();
}
void animateGif() {
tick();
SurfaceHolder surfaceHolder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
gifCanvas(canvas);
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
gifHandler.removeCallbacks(runnable);
if (isVisible()) {
gifHandler.postDelayed(runnable, 1000L / 25L);
}
}
void tick() {
if (when == -1L) {
when = 0;
start = SystemClock.uptimeMillis();
} else {
long diff = SystemClock.uptimeMillis() - start;
when = (int) (diff % duration);
}
}
void gifCanvas(Canvas canvas) {
canvas.save();
canvas.scale(scaleX, scaleY);
gif.setTime(when);
gif.draw(canvas, 0, 0);
canvas.restore();
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
stopSelf();
gifHandler.removeCallbacks(runnable);
}
}
}
Activity class for setting wallpaper
setWallpaper.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Build.VERSION.SDK_INT > 15) {
Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, new ComponentName(mContext, GifPaperService.class));
startActivity(intent);
}
}
});
ImageAdapter:
public class ImageAdapter extends BaseAdapter {
static WallpaperInfo info;
private Context mContext;
public ImageAdapter() {
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return mThumbIds[position];
}
public long getItemId(int position) {
return 0;
}
public ImageAdapter(Context c) {
mContext = c;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null){
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(3, 3, 3, 3);
imageView.setMaxHeight(300);
imageView.setMaxWidth(300);
imageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MyPreferenceActivity myPref = new MyPreferenceActivity(mContext);
myPref.setGifImage(position);
Intent intent = new Intent(mContext, FullScreenImage.class);
intent.putExtra("imageID", mThumbIds[position]);
/*intent.putExtra(EXTRA_LIVE_WALLPAPER_INTENT, intent);
intent.putExtra(EXTRA_LIVE_WALLPAPER_SETTINGS, info.getSettingsActivity());
intent.putExtra(EXTRA_LIVE_WALLPAPER_PACKAGE, info.getPackageName());*/
mContext.startActivity(intent);
}
});
Animation anim = AnimationUtils.loadAnimation(mContext.getApplicationContext(), R.anim.fly);
imageView.setAnimation(anim);
anim.start();
}
else{
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
public Integer[] mThumbIds = {
R.drawable.gpp1, R.drawable.gpp2,
R.drawable.gpp3,R.drawable.gpp4,
R.drawable.gpp5,R.drawable.gpp6,
R.drawable.gpp7,R.mipmap.h8,
R.mipmap.h9,R.mipmap.h10,
R.mipmap.h11,R.drawable.gp3,
R.drawable.gp2,R.drawable.gp,
R.drawable.onehalloween
};
}
If anyone know what is the problem.Tell me.
Thank in advance
For destroying previous wallpaper and setting new wallpaper,You have to Clear previous wallpaper like this,
In Your setWallpaper button click event use this code,
setWallpaper.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Build.VERSION.SDK_INT > 16) {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(getApplicationContext());
try {
wallpaperManager.clear();
} catch (IOException e) {
e.printStackTrace();
}
Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, new ComponentName(mContext, GifPaperService.class));
Log.i("Intent....", "...." + intent);
startActivity(intent);
}
}
});
This is works for me...
i have created an app in which there are 4 tabs in the action bar .
i have used a manuelpeinado.fadingactionbar .
while scrolling ,it works for the first tab but when i shift to second tab color is not changing .
i looked in to logcat and find that values of "mActionBarBackgroundDrawable.setAlpha(newAlpha);"
is changing but color is not changing.
its my 1st question on stackoverflow if i missed something then sorry for that .
thanks in ADV.
public class FadingActionBarHelper {
protected static final String TAG = "FadingActionBarHelper";
private Drawable mActionBarBackgroundDrawable;
private FrameLayout mHeaderContainer;
private int mActionBarBackgroundResId;
private int mHeaderLayoutResId;
private View mHeaderView;
private int mContentLayoutResId;
private View mContentView;
private ActionBar mActionBar;
private LayoutInflater mInflater;
private boolean mLightActionBar;
private boolean mUseParallax = true;
private int mLastDampedScroll;
private int mLastHeaderHeight = -1;
private ViewGroup mContentContainer;
private ViewGroup mScrollView;
private boolean mFirstGlobalLayoutPerformed;
private View mMarginView;
private View mListViewBackgroundView;
private double speed = 0;
public FadingActionBarHelper actionBarBackground(int drawableResId) {
mActionBarBackgroundResId = drawableResId;
return this;
}
public FadingActionBarHelper actionBarBackground(Drawable drawable) {
mActionBarBackgroundDrawable = drawable;
return this;
}
public FadingActionBarHelper headerLayout(int layoutResId) {
mHeaderLayoutResId = layoutResId;
return this;
}
public FadingActionBarHelper headerView(View view) {
mHeaderView = view;
return this;
}
public FadingActionBarHelper contentLayout(int layoutResId) {
mContentLayoutResId = layoutResId;
return this;
}
public FadingActionBarHelper contentView(View view) {
mContentView = view;
return this;
}
public FadingActionBarHelper lightActionBar(boolean value) {
mLightActionBar = value;
return this;
}
public FadingActionBarHelper parallax(boolean value) {
mUseParallax = value;
return this;
}
public double getSpeed(){
return speed;
}
public View createView(Context context) {
return createView(LayoutInflater.from(context));
}
public View createView(LayoutInflater inflater) {
//
// Prepare everything
mInflater = inflater;
if (mContentView == null) {
mContentView = inflater.inflate(mContentLayoutResId, null);
}
if (mHeaderView == null) {
mHeaderView = inflater.inflate(mHeaderLayoutResId, mHeaderContainer, false);
}
//
// See if we are in a ListView or ScrollView scenario
ListView listView = (ListView) mContentView.findViewById(android.R.id.list);
View root;
if (listView != null) {
root = createListView(listView);
} else {
root = createScrollView();
}
// Use measured height here as an estimate of the header height, later on after the layout is complete
// we'll use the actual height
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(LayoutParams.MATCH_PARENT, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY);
mHeaderView.measure(widthMeasureSpec, heightMeasureSpec);
updateHeaderHeight(mHeaderView.getMeasuredHeight());
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int headerHeight = mHeaderContainer.getHeight();
if (!mFirstGlobalLayoutPerformed && headerHeight != 0) {
updateHeaderHeight(headerHeight);
mFirstGlobalLayoutPerformed = true;
}
}
});
return root;
}
public void initActionBar(Activity activity) {
mActionBar = getActionBar(activity);
if (mActionBarBackgroundDrawable == null) {
mActionBarBackgroundDrawable = activity.getResources().getDrawable(mActionBarBackgroundResId);
}
mActionBar.setBackgroundDrawable(mActionBarBackgroundDrawable);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
mActionBarBackgroundDrawable.setCallback(mDrawableCallback);
Toast.makeText(activity, "Inside initActionBar version less than jellybean", Toast.LENGTH_SHORT);
}
Toast.makeText(activity, " Inside initActionBar ", Toast.LENGTH_SHORT);
mActionBarBackgroundDrawable.setAlpha(0);
}
protected ActionBar getActionBar(Activity activity) {
return activity.getActionBar();
}
private Drawable.Callback mDrawableCallback = new Drawable.Callback() {
#Override
public void invalidateDrawable(Drawable who) {
mActionBar.setBackgroundDrawable(who);
}
#Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
}
#Override
public void unscheduleDrawable(Drawable who, Runnable what) {
}
};
private View createScrollView() {
mScrollView = (ViewGroup) mInflater.inflate(R.layout.fab__scrollview_container, null);
NotifyingScrollView scrollView = (NotifyingScrollView) mScrollView.findViewById(R.id.fab__scroll_view);
scrollView.setOnScrollChangedListener(mOnScrollChangedListener);
mContentContainer = (ViewGroup) mScrollView.findViewById(R.id.fab__container);
mContentContainer.addView(mContentView);
mHeaderContainer = (FrameLayout) mScrollView.findViewById(R.id.fab__header_container);
initializeGradient(mHeaderContainer);
mHeaderContainer.addView(mHeaderView, 0);
mMarginView = mContentContainer.findViewById(R.id.fab__content_top_margin);
return mScrollView;
}
private NotifyingScrollView.OnScrollChangedListener mOnScrollChangedListener = new NotifyingScrollView.OnScrollChangedListener() {
public void onScrollChanged(ScrollView who, int l, int t, int oldl, int oldt) {
onNewScroll(t);
}
};
private View createListView(ListView listView) {
mContentContainer = (ViewGroup) mInflater.inflate(R.layout.fab__listview_container, null);
mContentContainer.addView(mContentView);
mHeaderContainer = (FrameLayout) mContentContainer.findViewById(R.id.fab__header_container);
initializeGradient(mHeaderContainer);
mHeaderContainer.addView(mHeaderView, 0);
mMarginView = new View(listView.getContext());
mMarginView.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, 0));
listView.addHeaderView(mMarginView, null, false);
// Make the background as high as the screen so that it fills regardless of the amount of scroll.
mListViewBackgroundView = mContentContainer.findViewById(R.id.fab__listview_background);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mListViewBackgroundView.getLayoutParams();
params.height = Utils.getDisplayHeight(listView.getContext());
mListViewBackgroundView.setLayoutParams(params);
listView.setOnScrollListener(new CustomScrollListener());
return mContentContainer;
}
private class CustomScrollListener implements OnScrollListener{
private int previousFirstVisibleItem = 0;
private long previousEventTime = 0, currTime, timeToScrollOneElement;
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
View topChild = null;
topChild = view.getChildAt(0);
if (topChild == null) {
onNewScroll(0);
} else if (topChild != mMarginView) {
onNewScroll(mHeaderContainer.getHeight());
} else {
onNewScroll(-topChild.getTop());
}
if (previousFirstVisibleItem != firstVisibleItem) {
currTime = System.currentTimeMillis();
timeToScrollOneElement = currTime - previousEventTime;
speed = ((double) 1 / timeToScrollOneElement) * 1000;
previousFirstVisibleItem = firstVisibleItem;
previousEventTime = currTime;
}
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
}
private int mLastScrollPosition ;
private void onNewScroll(int scrollPosition) {
if (mActionBar == null) {
return;
}
int currentHeaderHeight = mHeaderContainer.getHeight();
if (currentHeaderHeight != mLastHeaderHeight) {
updateHeaderHeight(currentHeaderHeight); // to make position of header at its default pos.
}
int headerHeight = currentHeaderHeight - mActionBar.getHeight();
float ratio = (float) Math.min(Math.max(scrollPosition, 0), headerHeight) / headerHeight;
int newAlpha = (int) (ratio * 255);
Log.d(" Inside onNewScroll() ", ""+newAlpha);
mActionBarBackgroundDrawable.setAlpha(newAlpha);
// addParallaxEffect(scrollPosition);
}
private void addParallaxEffect(int scrollPosition) {
float damping = mUseParallax ? 0.5f : 1.0f;
int dampedScroll = (int) (scrollPosition * damping);
int offset = mLastDampedScroll - dampedScroll;
Log.d(" Inside addParallaxEffect() ", ""+offset);
mHeaderContainer.offsetTopAndBottom(offset);
if (mListViewBackgroundView != null) {
offset = mLastScrollPosition - scrollPosition;
mListViewBackgroundView.offsetTopAndBottom(offset);
}
if (mFirstGlobalLayoutPerformed) {
mLastScrollPosition = scrollPosition;
mLastDampedScroll = dampedScroll;
}
}
private void updateHeaderHeight(int headerHeight) {
ViewGroup.LayoutParams params = (ViewGroup.LayoutParams) mMarginView.getLayoutParams();
params.height = headerHeight;
mMarginView.setLayoutParams(params);
if (mListViewBackgroundView != null) {
FrameLayout.LayoutParams params2 = (FrameLayout.LayoutParams) mListViewBackgroundView.getLayoutParams();
params2.topMargin = headerHeight;
mListViewBackgroundView.setLayoutParams(params2);
}
mLastHeaderHeight = headerHeight;
}
private void initializeGradient(ViewGroup headerContainer) {
View gradientView = headerContainer.findViewById(R.id.fab__gradient);
int gradient = R.drawable.fab__gradient;
if (mLightActionBar) {
gradient = R.drawable.fab__gradient_light;
}
gradientView.setBackgroundResource(gradient);
}
}
I have a bitmap, which is drawn in my Sprite class, this bitmap is more than one picture in one, so I can animate it.
When I inflate the GameLayout next to my XML, this sprite is drawn. Every time the run() method of my GameActivity is called (with special circumstances) this picture updates itself 4 times, so the animation is through.
This is all running perfectly, but my Problem is, that if I press the button (no matter if the curcumstances are fulfilled), the bitmap jumps to the next picture, so, the bitmap updates itself, without me calling it with view.invalidate().
Do you have any idea how I could stop this?
GameActivity:
public class GameActivity extends Activity implements OnClickListener, Runnable
{
private int punkte;
private int highscore;
private Handler handler = new Handler();
private int balken = 0;
private boolean abnehmend = false;
private int Klick = 0;
private boolean balkenAktiv = true;
private boolean gewartet = false;
private int versuche;
private View view;
private int counter = 0;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// inflate mainXML->
View mainView = getLayoutInflater().inflate(R.layout.activity_game, null);
// find container->
LinearLayout container = (LinearLayout) mainView.findViewById(R.id.container);
// initialize your custom view->
view = new GameLayout(this);
// add your custom view to container->
container.addView(view);
setContentView(mainView);
versuche = leseVersuche();
highscore = leseHighscore();
setupActionBar();
neueRunde();
Button button = (Button) findViewById(R.id.thebutton);
button.setOnClickListener(this);
}
/**
* Set up the {#link android.app.ActionBar}, if the API is available.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupActionBar()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.game, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
private void neueRunde()
{
punkte = 0;
refreshScreen();
}
private void refreshScreen()
{
TextView tvPunkte = (TextView) findViewById(R.id.points);
tvPunkte.setText("Punkte: " + Integer.toString(punkte));
TextView tvHighscores = (TextView) findViewById(R.id.highscores);
tvHighscores.setText("Highscore: " + Integer.toString(highscore));
TextView tvVersuche = (TextView) findViewById(R.id.versuches);
tvVersuche.setText("Versuche: " + Integer.toString(versuche));
}
#Override
public void onClick(View v)
{
if(Klick == 0)
{
handler.postDelayed(this, 0);
Klick = 1;
}
else
{
if(Klick == 1)
{
balkenAktiv = false;
handler.postDelayed(this, 500);
punkte = balken;
versuche++;
schreibeVersuche(versuche);
highscore += punkte;
schreibeHighscore(highscore);
Klick = 2;
refreshScreen();
}
else
{
if(Klick == 2)
{
Klick = 0;
punkte = 0;
balken = 0;
ProgressBar proBar = (ProgressBar) findViewById(R.id.sprung);
proBar.setProgress(balken);
refreshScreen();
}
}
}
}
#Override
public void run()
{
if(balkenAktiv == true)
{
gewartet = false;
ProgressBar proBar = (ProgressBar) findViewById(R.id.sprung);
if(abnehmend == false)
{
if(balken < 100)
{
balken+=5;
proBar.setProgress(balken);
refreshScreen();
handler.postDelayed(this, 0);
}
else if(balken == 100)
{
abnehmend = true;
proBar.setProgress(balken);
refreshScreen();
handler.postDelayed(this, 0);
}
}
else if(abnehmend == true)
{
if(balken > 0)
{
balken-=5;
proBar.setProgress(balken);
refreshScreen();
handler.postDelayed(this, 0);
}
else if (balken == 0)
{
abnehmend = false;
proBar.setProgress(balken);
refreshScreen();
handler.postDelayed(this, 0);
}
}
}
else if (balkenAktiv == false)
{
if(gewartet == true)
{
if(counter < 4)
{
counter++;
view.invalidate();
handler.postDelayed(this, 500);
}
else if (counter >= 4)
{
balkenAktiv = true;
counter = 0;
}
}
gewartet = true;
}
}
private void schreibeHighscore(int highscore)
{
SharedPreferences pref = getSharedPreferences("GAME", 0);
SharedPreferences.Editor editor = pref.edit();
editor.putInt("HIGHSCORE", highscore);
editor.commit();
}
private void schreibeVersuche(int versuche)
{
SharedPreferences pref = getSharedPreferences("GAME", 0);
SharedPreferences.Editor editor = pref.edit();
editor.putInt("VERSUCHE", versuche);
editor.commit();
}
private int leseVersuche()
{
SharedPreferences pref = getSharedPreferences("GAME", 0);
return pref.getInt("VERSUCHE", 0);
}
private int leseHighscore()
{
SharedPreferences pref = getSharedPreferences("GAME", 0);
return pref.getInt("HIGHSCORE", 0);
}
}
onDraw of View Class:
#Override
protected void onDraw(Canvas canvas)
{
sprite.draw(canvas);
sprite.update(System.currentTimeMillis());
}
The Sprite Class:
public class Sprite
{
//private static final String TAG = Sprite.class.getSimpleName();
private Bitmap bitmap; // the animation sequence
private Rect sourceRect; // the rectangle to be drawn from the animation bitmap
private int frameNr; // number of frames in animation
private int currentFrame; // the current frame
private long frameTicker; // the time of the last frame update
private int framePeriod; // milliseconds between each frame (1000/fps)
private int spriteWidth; // the width of the sprite to calculate the cut out rectangle
private int spriteHeight; // the height of the sprite
private int x; // the X coordinate of the object (top left of the image)
private int y; // the Y coordinate of the object (top left of the image)
public Sprite(Bitmap bitmap, int x, int y, int width, int height, int fps, int frameCount)
{
this.bitmap = bitmap;
this.x = x;
this.y = y;
currentFrame = 0;
frameNr = frameCount;
spriteWidth = bitmap.getWidth() / frameCount;
spriteHeight = bitmap.getHeight();
sourceRect = new Rect( 0, 0, spriteWidth, spriteHeight);
framePeriod = 1000 / fps;
frameTicker = 0l;
}
public void update(long gameTime)
{
if (gameTime > frameTicker + framePeriod)
{
frameTicker = gameTime;
// increment the frame
currentFrame++;
if (currentFrame >= frameNr)
{
currentFrame = 0;
}
}
// define the rectangle to cut out sprite
this.sourceRect.left = currentFrame * spriteWidth;
this.sourceRect.right = this.sourceRect.left + spriteWidth;
}
public void draw(Canvas canvas)
{
// where to draw the sprite
Rect destRect = new Rect( x, y, x + spriteWidth, y + spriteHeight);
canvas.drawBitmap(bitmap, sourceRect, destRect, null);
}
}
The solution for this was pretty easy:
Instead of using view.invalidate() in my run() method I used
((GameLayout) view).update(System.currentTimeMillis()) so I call the update() isntead of the onDraw() of my GameLayout class.
With this change I could delete the update() method out of my onDraw() so if onDraw() is getting updated the picture will update, but will not change.
Then I added invalidate() to my update() method so it would redraw itself after updating.
Now everything is running smooooooothly.