I am creating a moving animation for my Card Game, for this i have created a custom surface view, while calling invalidate method inside my Surface View i am getting following exception
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
My code:
Thread Class
class MySurfaceViewThread:BaseThread
{
private MySurfaceView mysurfaceview;
private ISurfaceHolder myThreadSurfaceHolder;
bool running;
public MySurfaceViewThread(ISurfaceHolder paramSurfaceHolder, MySurfaceView paramSurfaceView)
{
mysurfaceview = paramSurfaceView;
myThreadSurfaceHolder = paramSurfaceHolder;
}
public override void RunThread()
{
Canvas c;
while (running)
{
c = null;
try
{
c = myThreadSurfaceHolder.LockCanvas(null);
mysurfaceview.Render(c);
mysurfaceview.PostInvalidate();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
finally
{
if (c != null)
{
myThreadSurfaceHolder.UnlockCanvasAndPost(c);
}
// running = false;
}
}
}
public override void SetRunning(bool paramBoolean)
{
running = paramBoolean;
}
}
Surface View Class
class MySurfaceView : SurfaceView, ISurfaceHolderCallback
{
ISurfaceHolder holder;
MySurfaceViewThread thread;
Context context;
Deck DealtDeck;
DisplayMetrics metrics;
int Screen_Center_X;
int Screen_Center_Y;
int Screen_Width;
int Screen_Height;
int Screen_Top_Middle_X;
int Screen_Top_Middle_Y;
int Screen_Bottom_Middle_X;
int Screen_Bottom_Middle_Y;
float density;
int Card_Width;
int Card_Height;
int Down_Card_Gap;
Deck DiscardedDeck;
Deck MainPlayer;
int localdownanimationvalue=0;
Bitmap localimage;
Bitmap rotatedimage;
Cards localcard;
public MySurfaceView(Context context):base(context)
{
this.context = context;
metrics = Resources.DisplayMetrics;
SetWillNotDraw(false);
Init();
}
public MySurfaceView(Context context, IAttributeSet attrs):base(context, attrs)
{
this.context=context;
metrics = Resources.DisplayMetrics;
SetWillNotDraw(false);
Init();
}
private void Init()
{
Console.WriteLine("Init method start");
// SurfaceView surfaceview = this;
holder = Holder;
holder.AddCallback(this);
this.thread = new MySurfaceViewThread(holder,this);
Focusable=true;
}
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
//throw new NotImplementedException();
}
public void SurfaceCreated(ISurfaceHolder holder)
{
this.thread.SetRunning(true);
this.thread.Start();
Initializevariable();
AllocatedCardList();
SetWillNotDraw(false);
}
private void Initializevariable()
{
Screen_Width = metrics.WidthPixels;
Screen_Height = metrics.HeightPixels;
density = metrics.Density;
Card_Width = (int)(125.0F * density);
Card_Height = (int)(93.0F * density);
Screen_Center_X = Screen_Width / 2;
Screen_Center_Y = Screen_Height / 2;
Screen_Top_Middle_X = Screen_Center_X - Card_Width;
Screen_Top_Middle_Y = Screen_Center_Y - Card_Height;
Screen_Bottom_Middle_X = Screen_Center_X - Card_Width/2;
Screen_Bottom_Middle_Y = Screen_Height - Card_Height;
DealtDeck = new Deck();
MainPlayer = new Deck();
// FaceDownDeck = new Deck(Screen_Center_X - Card_Width/2, Screen_Center_Y- Card_Height/2);
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
bool retry = true;
this.thread.SetRunning(false);
while(retry)
{
thread.Join();
retry = false;
}
}
void AllocatedCardList()
{
Cards localcard;
//Allocate all cards to dealtdeck first
for (int i = 1; i <= 13; i++)
{
for (int j = 1; j <= 4; j++)
{
DealtDeck.Add(new Cards((Cards.Rank)i, (Cards.SuitType)j, true, (Screen_Center_X - Card_Width / 2), (Screen_Center_Y - Card_Height / 2)));
}
}
//Allocate to bottom player starting card should be bottom-center
localcard = DealtDeck.RemoveCard();
localcard.current_X = Screen_Bottom_Middle_X;
localcard.current_Y = Screen_Bottom_Middle_Y;
MainPlayer.Add(localcard);
}
public void Render(Canvas paramCanvas)
{
try
{
//
localcard = DealtDeck.getCard();
if (localdownanimationvalue <= Screen_Height)
{
paramCanvas.DrawColor(Android.Graphics.Color.Transparent, PorterDuff.Mode.Clear);
localimage = DecodeSampledBitmapFromResource(Resources, localcard.GetImageId(context), Card_Width, Card_Height);
rotatedimage = RotateBitmap(localimage, 180);
paramCanvas.DrawBitmap(rotatedimage, Screen_Center_X, localdownanimationvalue, null);
Updatedowncardvalue();
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
protected override void OnDraw(Canvas paramCanvas)
{
}
private void Updatedowncardvalue()
{
const int updatevalue = 50;
if (localdownanimationvalue + updatevalue > Screen_Height)
localdownanimationvalue = Screen_Height;
else
localdownanimationvalue = localdownanimationvalue + updatevalue;
Invalidate();
}
private Bitmap DecodeSampledBitmapFromResource(Resources resources, int cardid, int card_Width, int card_Height)
{
BitmapFactory.Options options = new BitmapFactory.Options
{
InJustDecodeBounds = true
};
Bitmap image = BitmapFactory.DecodeResource(resources, cardid,options);
options.InSampleSize = CalculateInSampleSize(options, card_Width, card_Height);
options.InJustDecodeBounds = false;
return BitmapFactory.DecodeResource(resources, cardid, options);
}
private int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
int width = options.OutWidth;
int height = options.OutHeight;
int samplesize = 1;
if(height > reqHeight || width > reqWidth)
{
// Calculate ratios of height and width to requested height and width
int heightratio = (int)Math.Round((double)height / reqHeight);
int widthratio = (int)Math.Round((double)width / reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
samplesize = heightratio < widthratio ? widthratio : heightratio;
}
return samplesize;
}
private Bitmap RotateBitmap(Bitmap localimage, float angle)
{
Matrix matrix = new Matrix();
matrix.PostRotate(angle);
Bitmap resized= Bitmap.CreateBitmap(localimage, 0, 0, localimage.Width, localimage.Height, matrix, true);
localimage.Recycle();
return resized;
}
}
Stacktrace:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6462)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:932)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:4692)
at android.view.View.invalidateInternal(View.java:11806)
at android.view.View.invalidate(View.java:11770)
at android.view.View.invalidate(View.java:11754)
Only the original thread that created a view hierarchy can touch its views.
You need to use RunOnUiThread from within your thread code whenever you update your views:
RunOnUiThread (() => {
someView.SomeProperty = "SO";
});
re: https://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)
Related
I want to create 2 buttons : when I click to button1 the model1 is initialized and when I click on button2 the model2 is initialized so I created new activity (MainSecond.java) where I created the 2 buttons and send their ids to MainActivity where the 2 models initialized
the problem is when I click on any of the two buttons the 2 models are initialized
this is my code :
MainSecond.java
public class MainSecond extends Activity {
public Button button1;
public Button button2;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_main);
button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
BackToMain(R.id.button1);
// BackToMain(view);
}
});
button2 = (Button) findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
BackToMain(R.id.button1);
}
});
}
public void BackToMain(int button_id) {
Intent intent = new Intent(MainSecond.this, MainActivity.class);
intent.putExtra("name",button_id);
intent.putExtra("name",button_id);
startActivity(intent);
}
MainActivity.java
initialization of 2 models
public class InitializeModelAsyncTask extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void... voids) {
final boolean ret = DeeplabModel.initialize();
Logger.debug("initialize deeplab model: %s", ret);
return ret;
}
}
public class InitializeModelAsyncTask2 extends AsyncTask<Void, Void, Boolean> {
#Override
protected Boolean doInBackground(Void... voids) {
final boolean ret2 = DeeplabModel2.initialize();
Logger.debug("initialize deeplab model: %s", ret2);
return ret2;
}
}
getting buttons ids :
public void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
buckyButton = findViewById(R.id.buckysButton);
// src_img =(ImageView) findViewById(R.id.src_img) ;
Intent mIntent=getIntent();
int intval=mIntent.getIntExtra("buttonid",0);
if(intval==R.id.button1){
initModel();
}
if(intval==R.id.button2){
initModel2();
}
}
private void syncUIWithPermissions(boolean requestIfNeed) {
final boolean granted = checkRequiredPermissions(requestIfNeed);
setPickImageEnabled(granted);
setPickImageEnabled2(granted);
if (granted && !DeeplabModel.isInitialized()) {
initModel();
}
else if (granted && !DeeplabModel2.isInitialized()) {
initModel2();
}
}
private boolean checkRequiredPermissions() {
return checkRequiredPermissions(false);
}
private boolean checkRequiredPermissions(boolean requestIfNeed) {
final boolean writeStoragePermGranted =
ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED;
Logger.debug("storage permission granted: %s", writeStoragePermGranted);
if (!writeStoragePermGranted
&& requestIfNeed) {
requestRequiredPermissions();
}
return writeStoragePermGranted;
}
private void requestRequiredPermissions() {
ActivityCompat.requestPermissions(this,
new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE,
},
REQUEST_REQUIRED_PERMISSION);
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
Logger.debug("requestCode = 0x%02x, permission = [%s], grant = [%s]",
requestCode,
ArrayUtils.stringArrayToString(permissions, ","),
ArrayUtils.intArrayToString(grantResults));
if (requestCode == REQUEST_REQUIRED_PERMISSION) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Logger.debug("permission granted, initialize model.");
initModel();
initModel2();
}
the initMode() and intitModel2() functions are :
private void initModel() {
new InitializeModelAsyncTask().execute((Void)null);
}
private void initModel2() {
new InitializeModelAsyncTask2().execute((Void)null);
}
this is the code that is used to show the 2 models on the screen
public class SegmentBitmapsLoader extends AbsAsyncDataLoader<List<SegmentBitmap>> {
private Uri mImageUri;
public SegmentBitmapsLoader(Context context, Uri imageUri) {
super(context);
mImageUri = imageUri;
}
#Nullable
#Override
public List<SegmentBitmap> loadInBackground() {
final Context context = getContext();
if (context == null) {
return null;
}
final Resources res = context.getResources();
if (res == null) {
return null;
}
if (mImageUri == null) {
return null;
}
final String filePath = FilePickUtils.getPath(context, mImageUri);
Logger.debug("file to mask: %s", filePath);
if (TextUtils.isEmpty(filePath)) {
return null;
}
boolean vertical = checkAndReportDimen(filePath);
final int dw = res.getDimensionPixelSize(
vertical ? R.dimen.image_width_v : R.dimen.image_width_h);
final int dh = res.getDimensionPixelSize(
vertical ? R.dimen.image_height_v : R.dimen.image_height_h);
Logger.debug("display image dimen: [%d x %d]", dw, dh);
Bitmap bitmap = decodeBitmapFromFile(filePath, dw, dh);
if (bitmap == null) {
return null;
}
List<SegmentBitmap> bitmaps = new ArrayList<>();
bitmaps.add(new SegmentBitmap(R.string.label_original, bitmap));//important note
final int w = bitmap.getWidth();
final int h = bitmap.getHeight();
Logger.debug("decoded file dimen: [%d x %d]", w, h);
EventBus.getDefault().post(new ImageDimenEvent(mImageUri, w, h));
float resizeRatio = (float) DeeplabModel.INPUT_SIZE / Math.max(bitmap.getWidth(), bitmap.getHeight());
float resizeRatio2 = (float) DeeplabModel2.INPUT_SIZE / Math.max(bitmap.getWidth(), bitmap.getHeight());
int rw = Math.round(w * resizeRatio);
int rh = Math.round(h * resizeRatio);
int rw2 = Math.round(w * resizeRatio2);
int rh2 = Math.round(h * resizeRatio2);
Logger.debug("resize bitmap: ratio = %f, [%d x %d] -> [%d x %d]",
resizeRatio, w, h, rw, rh);
Logger.debug("resize bitmap: ratio = %f, [%d x %d] -> [%d x %d]",
resizeRatio2, w, h, rw2, rh2);
Bitmap resized = ImageUtils.tfResizeBilinear(bitmap, rw, rh);
Bitmap resized2 = ImageUtils.tfResizeBilinear(bitmap, rw2, rh2);
Bitmap mask = DeeplabModel.segment(resized);
Bitmap mask2 = DeeplabModel2.segment(resized2);
if (mask != null) {
mask = BitmapUtils.scaleBitmap(mask, w, h);
bitmaps.add(new SegmentBitmap(R.string.label_mask, mask));
final Bitmap cropped = cropBitmapWithMask(bitmap, mask);
bitmaps.add(new SegmentBitmap(R.string.label_cropped, cropped));
}
else {
bitmaps.add(new SegmentBitmap(R.string.label_mask, (Bitmap) null));
bitmaps.add(new SegmentBitmap(R.string.label_cropped, (Bitmap) null));
}
if(mask2 != null){
mask2 = BitmapUtils.scaleBitmap(mask2, w, h);
bitmaps.add(new SegmentBitmap(R.string.label_mask, mask2));
final Bitmap cropped = cropBitmapWithMask(bitmap, mask2);
bitmaps.add(new SegmentBitmap(R.string.label_cropped, cropped));
}
else {
bitmaps.add(new SegmentBitmap(R.string.label_mask, (Bitmap)null));
bitmaps.add(new SegmentBitmap(R.string.label_cropped, (Bitmap)null));
}
return bitmaps;
}
private boolean checkAndReportDimen(String filePath) {
if (TextUtils.isEmpty(filePath)) {
return false;
}
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
final int width = options.outWidth;
final int height = options.outHeight;
Logger.debug("original image dimen: %d x %d", width, height);
EventBus.getDefault().post(new ImageDimenEvent(mImageUri, width, height));
return (height > width);
}
private Bitmap cropBitmapWithMask(Bitmap original, Bitmap mask) {
if (original == null
|| mask == null) {
return null;
}
final int w = original.getWidth();
final int h = original.getHeight();
if (w <= 0 || h <= 0) {
return null;
}
Bitmap cropped = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(cropped);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(original, 0, 0, null);
canvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
return cropped;
}
public static Bitmap decodeBitmapFromFile(String filePath,
int reqWidth,
int reqHeight) {
if (TextUtils.isEmpty(filePath)) {
return null;
}
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
Well you are sending the same button id for both the buttons...
When you call BacktoMain method for both the buttons you are sending button1 id.
Change the line inside button2.onClickListener from
BackToMain(R.id.button1);
to
BackToMain(R.id.button2);
Do this...
public void BackToMain(int button_id) {
Intent intent = new Intent(MainSecond.this, MainActivity.class);
intent.putExtra("name",button_id);
intent.putExtra("name",button_id); //remove this line y do the same thing twice
startActivity(intent);
}
And...
Intent mIntent=getIntent();
int intval=mIntent.getIntExtra("name",0);
//should give you the button id and returns 0 if
//value for the key "name" was not given
if(intval==R.id.button1){
initModel();
}
if(intval==R.id.button2){
initModel2();
}
Try these changes and lemme know if it works..
I wrote a simple application that sets the wallpaper on the device. I can't achieve one effect. I wish the picture automatically centrated horizontally. This means that the center of the image was on the most central desktop of Luncher app.
The picture at the bottom shows how it looks now:
Effect that I want to achieve:
And the image itself:
I tried to use the code from this question, however, did not achieve the desired effect.
My code:
public class SystemWallpaperHelper {
private Context context;
private ImageLoader imageLoader;
private DisplayImageOptions imageLoaderOptions;
public SystemWallpaperHelper(Context context){
this.context = context;
setImageLoaderOptions();
}
private void setImageLoaderOptions() {
final int width = SharedHelper.getDeviceWidth(context) << 1 ; // best wallpaper width is twice screen width
imageLoaderOptions = new DisplayImageOptions.Builder()
.imageScaleType(ImageScaleType.NONE)
.cacheInMemory(false)
.cacheOnDisk(false)
.postProcessor(new BitmapProcessor() {
#Override
public Bitmap process(Bitmap bmp) {
float scale = (float) width / bmp.getWidth() ;
int height = (int) (scale * bmp.getHeight());
return Bitmap.createScaledBitmap(bmp, width, height, false);
}
})
.build();
imageLoader = ImageLoader.getInstance();
}
public void setDeviceWallpaper(Wallpaper wallpaper){
imageLoader.loadImage(wallpaper.getSrcUrl(), imageLoaderOptions, new SimpleImageLoadingListener(){
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage)
{
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
try {
wallpaperManager.setBitmap(loadedImage);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
After several attempts, I managed to achieve the desired effect.
public class SystemWallpaperHelper {
private Context context;
private ImageLoader imageLoader;
private DisplayImageOptions imageLoaderOptions;
private WallpaperManager wallpaperManager;
public SystemWallpaperHelper(Context context) {
this.context = context;
setImageLoaderOptions();
wallpaperManager = WallpaperManager.getInstance(context);
}
private void setImageLoaderOptions() {
imageLoaderOptions = new DisplayImageOptions.Builder()
.imageScaleType(ImageScaleType.NONE)
.cacheInMemory(false)
.cacheOnDisk(false)
.postProcessor(new BitmapProcessor() {
#Override
public Bitmap process(Bitmap bmp) {
return centerCropWallpaper(bmp, wallpaperManager.getDesiredMinimumWidth(), wallpaperManager.getDesiredMinimumHeight());
}
})
.build();
imageLoader = ImageLoader.getInstance();
}
private Bitmap centerCropWallpaper(Bitmap wallpaper, int desiredWidth, int desiredHeight){
float scale = (float) desiredHeight / wallpaper.getHeight();
int scaledWidth = (int) (scale * wallpaper.getWidth());
int deviceWidth = SharedHelper.getDeviceWidth(context);
int imageCenterWidth = scaledWidth /2;
int widthToCut = imageCenterWidth - deviceWidth / 2;
int leftWidth = scaledWidth - widthToCut;
Bitmap scaledWallpaper = Bitmap.createScaledBitmap(wallpaper, scaledWidth, desiredHeight, false);
Bitmap croppedWallpaper = Bitmap.createBitmap(
scaledWallpaper,
widthToCut,
0,
leftWidth,
desiredHeight
);
return croppedWallpaper;
}
public void setDeviceWallpaper(final Wallpaper wallpaper, final boolean adjusted) {
imageLoader.loadImage(wallpaper.getSrcUrl(), imageLoaderOptions, new SimpleImageLoadingListener() {
#TargetApi(Build.VERSION_CODES.KITKAT)
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
if (adjusted) {
wallpaperManager.getCropAndSetWallpaperIntent(SharedHelper.getImageUriForBitmap(context, loadedImage));
} else {
try {
int width = wallpaperManager.getDesiredMinimumWidth();
int height = wallpaperManager.getDesiredMinimumHeight();
int bitWidth = loadedImage.getWidth();
wallpaperManager.setBitmap(loadedImage);
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
}
Here is a more generic version, that can be pasted in any java android class. As an addition, it is not dependend on the display orientation.
public static void setWallpaper(Context context, BitmapDrawable wallpaper) {
try {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
if(wallpaper != null) {
Bitmap bmp = wallpaper.getBitmap();
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(metrics);
int height = metrics.heightPixels;
int width = metrics.widthPixels;
wallpaperManager.setWallpaperOffsetSteps(1, 1);
wallpaperManager.suggestDesiredDimensions(width, height);
Bitmap bitmap = centerCropWallpaper(context, bmp, Math.min(wallpaperManager.getDesiredMinimumWidth(), wallpaperManager.getDesiredMinimumHeight()));
wallpaperManager.setBitmap(bitmap);
} else {
Log.e(TAG, "wallpaper could not be set.");
}
} catch (Exception ex) {
Log.e(TAG, "error setting wallpaper. " + ex.getMessage(), ex);
}
}
private static Bitmap centerCropWallpaper(Context context, Bitmap wallpaper, int desiredHeight){
float scale = (float) desiredHeight / wallpaper.getHeight();
int scaledWidth = (int) (scale * wallpaper.getWidth());
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(metrics);
int deviceWidth = metrics.widthPixels;
int imageCenterWidth = scaledWidth /2;
int widthToCut = imageCenterWidth - deviceWidth / 2;
int leftWidth = scaledWidth - widthToCut;
Bitmap scaledWallpaper = Bitmap.createScaledBitmap(wallpaper, scaledWidth, desiredHeight, false);
return Bitmap.createBitmap(scaledWallpaper, widthToCut, 0, leftWidth, desiredHeight);
}
I am trying to set a sprite image as a background
and I didn't success to set the image size to screen size.
I'm trying this:
public class Game extends SurfaceView implements Runnable {
private SurfaceHolder holder;
private boolean isRunning = false;
private Thread gameThread;
private Sprite s;
private int screenWidth;
private int screenHeight;
Canvas canvas;
// private Sprite[] sprites;
private final static int MAX_FPS = 40; //desired fps
private final static int FRAME_PERIOD = 1000 / MAX_FPS; // the frame period
public Game(Context context) {
super(context);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
screenWidth = width;
screenHeight = height;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
//====== not working
// Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
// int sWidth = display.getWidth();
// int sHeight = display.getHeight();
//====== not working
// DisplayMetrics dm = new DisplayMetrics();
// ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
// int sWidth = dm.widthPixels;
// int sHeight = dm.heightPixels;
//====== not working
// screenHeight=canvas.getHeight();
// screenWidth=canvas.getWidth();
s=new Sprite(0, 0, BitmapFactory.decodeResource(this.getResources(), R.mipmap.back));
}
/**
* Start or resume the game.
*/
public void resume() {
isRunning = true;
gameThread = new Thread(this);
gameThread.start();
}
/**
* Pause the game loop
*/
public void pause() {
isRunning = false;
boolean retry = true;
while (retry) {
try {
gameThread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
}
class Sprite {
int x;
int y;
int directionX = 1;
int directionY = 1;
int speed = 10;
int color = 0;
Bitmap image;
public Sprite(int x, int y) {
this.x = x;
this.y = y;
}
public Sprite(int x, int y, Bitmap image) {
this(x, y);
this.image = image;
}
public Sprite(int x, int y, Bitmap image, int color) {
this(x, y, image);
this.color = color;
}
}
protected void step()
{
//blablabla
}
protected void render(Canvas canvas) {
canvas.drawColor(Color.BLACK);
Paint p = new Paint();
canvas.drawBitmap(s.image,s.x,s.y,p);
}
#Override
public void run() {
while(isRunning) {
// We need to make sure that the surface is ready
if (! holder.getSurface().isValid()) {
continue;
}
long started = System.currentTimeMillis();
// update
step();
// draw
canvas = holder.lockCanvas();
if (canvas != null) {
render(canvas);
holder.unlockCanvasAndPost(canvas);
}
float deltaTime = (System.currentTimeMillis() - started);
int sleepTime = (int) (FRAME_PERIOD - deltaTime);
if (sleepTime > 0) {
try {
gameThread.sleep(sleepTime);
}
catch (InterruptedException e) {
}
}
while (sleepTime < 0) {
step();
sleepTime += FRAME_PERIOD;
}
}
}
}
This options doesn't work:
//====== not working
// Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
// int sWidth = display.getWidth();
// int sHeight = display.getHeight();
//====== not working
// DisplayMetrics dm = new DisplayMetrics();
// ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
// int sWidth = dm.widthPixels;
// int sHeight = dm.heightPixels;
//====== not working
// screenHeight=canvas.getHeight();
// screenWidth=canvas.getWidth();
So how I can get the canvas or current screen size?
And set the Sprite.image to full screen ?
Try this for size in pixels,
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Try this when you decode your bitmap.
final int width = context.getResources().getDisplayMetrics().widthPixels;
final int height = context.getResources().getDisplayMetrics().heightPixels;
BitmapFactory.Options options = new BitmapFactory.Options();
options.outHeight = height;
options.outWidth = width;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher, options)
I'm new in Android Programming I'm Trying to make slideshow animated live wallpaper and all ok but the problem is when I set the wallpaper the scale of image is stretched to screen I want it to scale to all the phone screens and when swipe the wallpaper get the right part of image I Want Advice about this problem.
my code is :
public class CustomWallpaper extends WallpaperService {
#Override
public Engine onCreateEngine() {
return new WallpaperEngine();
}
class WallpaperEngine extends Engine {
//Duration between slides in milliseconds
private final int SLIDE_DURATION = 8;
private int[] mImagesArray;
private int mImagesArrayIndex = 0;
private Thread mDrawWallpaper;
private String mImageScale = "Fit to screen";
private CustomWallpaperHelper customWallpaperHelper;
public WallpaperEngine() {
customWallpaperHelper = new CustomWallpaperHelper(getApplicationContext(), getResources());
mImagesArray = new int[] {R.drawable.image_1,R.drawable.image_2,R.drawable.image_3,R.drawable.image_4,R.drawable.image_5,R.drawable.image_6,R.drawable.image_7,R.drawable.image_8,R.drawable.image_9,R.drawable.image_10,R.drawable.image_11,R.drawable.image_12,R.drawable.image_13,R.drawable.image_14,R.drawable.image_15,R.drawable.image_16,R.drawable.image_17,R.drawable.image_18,R.drawable.image_19,R.drawable.image_20,R.drawable.image_21,R.drawable.image_22,R.drawable.image_23,R.drawable.image_24,R.drawable.image_25,R.drawable.image_26,R.drawable.image_27,R.drawable.image_28,R.drawable.image_29,R.drawable.image_30,R.drawable.image_31,R.drawable.image_32,R.drawable.image_33,R.drawable.image_34,R.drawable.image_35,R.drawable.image_36,R.drawable.image_37,R.drawable.image_38,R.drawable.image_39,R.drawable.image_40,R.drawable.image_41};
mDrawWallpaper = new Thread(new Runnable() {
#Override
public void run() {
try {
while (true) {
drawFrame();
incrementCounter();
Thread.sleep(SLIDE_DURATION);
}
} catch (Exception e) {
//
}
}
});
mDrawWallpaper.start();
}
private void incrementCounter() {
mImagesArrayIndex++;
if (mImagesArrayIndex >= mImagesArray.length) {
mImagesArrayIndex = 0;
}
}
private void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas != null) {
drawImage(canvas);
}
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
private void drawImage(Canvas canvas) {
//Get the image and resize it
Bitmap image = BitmapFactory.decodeResource(getResources(),
mImagesArray[mImagesArrayIndex]);
//Draw background
customWallpaperHelper.setBackground(canvas);
//Scale the canvas
PointF mScale = customWallpaperHelper.getCanvasScale(mImageScale, image.getWidth(), image.getHeight());
canvas.scale(mScale.x, mScale.y);
//Draw the image on screen
Point mPos = customWallpaperHelper.getImagePos(mScale, image.getWidth(), image.getHeight());
canvas.drawBitmap(image, mPos.x, mPos.y, null);
}
}
}
and the other class is:
public class CustomWallpaperHelper {
public final static String IMAGE_SCALE_STRETCH_TO_SCREEN = "Stretch to screen";
public final static String IMAGE_SCALE_FIT_TO_SCREEN = "Fit to screen";
private Context mContext;
private Resources mResources;
private Point screenSize = new Point();
private Bitmap bgImageScaled;
private Point bgImagePos = new Point(0, 0);
public CustomWallpaperHelper(Context mContext, Resources mResources) {
this.mContext = mContext;
this.mResources = mResources;
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
screenSize.x = display.getWidth();
screenSize.y = display.getHeight();
;
}
private void scaleBackground() {
String imageScale = "Stretch to screen";
Bitmap bgImage = null;
if (imageScale.equals(IMAGE_SCALE_STRETCH_TO_SCREEN)) {
bgImagePos = new Point(0, 0);
bgImageScaled = Bitmap.createScaledBitmap(bgImage, screenSize.x, screenSize.y, true);
}
}
public void setBackground(Canvas canvas) {
if (bgImageScaled != null) {
canvas.drawBitmap(bgImageScaled, bgImagePos.x, bgImagePos.y, null);
} else {
canvas.drawColor(0xff000000);
}
}
public int getScreenWidth() {
return screenSize.x;
}
public int getScreenHeight() {
return screenSize.y;
}
public Point getImagePos(PointF canvasScale, int imageWidth, int imageHeight) {
Point imagePos = new Point();
imagePos.x = (int) (screenSize.x - (imageWidth * canvasScale.x)) / 2;
imagePos.y = (int) (screenSize.y - (imageHeight * canvasScale.y)) / 2;
return imagePos;
}
public PointF getCanvasScale(String imageScale, int imageWidth, int imageHeight) {
PointF canvasScale = new PointF(1f, 1f);
if (imageScale.equals(IMAGE_SCALE_STRETCH_TO_SCREEN)) {
canvasScale.x = getScreenWidth() / (1f * imageWidth);
canvasScale.y = getScreenHeight() / (1f * imageHeight);
} else {
boolean tooWide = false;
boolean tooTall = false;
if (getScreenWidth() < imageWidth) {
tooWide = true;
}
if (getScreenHeight() < imageHeight) {
tooTall = true;
}
if (tooWide && tooTall) {
int x = imageWidth / getScreenWidth();
int y = imageHeight / getScreenHeight();
if (x > y) {
canvasScale.x = getScreenWidth() / (1f * imageWidth);
canvasScale.y = 1;
} else {
canvasScale.x = 1;
canvasScale.y = getScreenHeight() / (1f * imageHeight);
}
} else if (tooWide) {
canvasScale.x = getScreenWidth() / (1f * imageWidth);
canvasScale.y = 1;
} else if (tooTall) {
canvasScale.x = 1;
canvasScale.y = getScreenHeight() / (1f * imageHeight);
}
}
return canvasScale;
}
}
I want Advice for this problem.
Thanks.
no need to do anything just Replace your below method with my code.
private void drawImage(Canvas canvas)
{
Bitmap image = BitmapFactory.decodeResource(getResources(),
mImagesArray[mImagesArrayIndex]);
Bitmap b=Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), true);
canvas.drawBitmap(b, 0,0, null);
}
I would suggest that you crop the images instead of resizing them. Something like:
Rect r = new Rect(left, top, right, bottom);
Bitmap croppedImage = null;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1){
InputStream in = mContentResolver.openInputStream(mSaveUri);
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(in, false);
croppedImage = decoder.decodeRegion(r, null);
} else {
final int width = r.width();
final int height = r.height();
croppedImage = Bitmap.createBitmap(mBitmap, r.left, r.top, width, height);
croppedImage.setDensity(croppedImage.getDensity() * mOutputX / width);
}
return croppedImage;
Hope this helps...
I am baffled by this error -
Only the original thread that created a view hierarchy can touch its views.
I have a class, which is called within a runnable/thread block in the UI. No attempt - as far as I can see ??? - is made to manipulate the UI within that runnable, or the class it calls, as below.....
public class MonthSort {
Handler handler;
int imageWidth;
List<PhotoData> photoList;
public MonthSort(Handler handler2, int width, List<PhotoData> pList) {
photoList = new ArrayList<PhotoData>();
photoList = pList;
imageWidth = width;
handler = handler2;
}
public void sortFiles()
{
int month, photoCount;
File fileName = new File("");
Message msg = handler.obtainMessage();
for (int i = 0; i < 12; i++) {
month = i + 1;
photoCount = 0;
for (PhotoData pd : photoList) {
if(month == pd.month)
{
if(photoCount == 0)
fileName = pd.fileName;
photoCount++;
}
}
if(photoCount != 0)
{
Bundle bundle = new Bundle();
bundle.putString("filename", fileName.toString());
bundle.putInt("month", month);
bundle.putInt("count", photoCount);
byte[] thumbNail = getThumbnail(fileName, imageWidth);
bundle.putByteArray("thumbnail", thumbNail);
msg.setData(bundle);
handler.dispatchMessage(msg);
}
}
Bundle bundle = new Bundle();
bundle.putBoolean("end", true);
msg.setData(bundle);
handler.dispatchMessage(msg);
}
private byte[] getThumbnail(File file, int size)
{
/** The object of this code is to reduce the bitmap for thumbnail display,
* Not just to reduce dimensions, but to reduce the physical size of the
* bitmap ready, so that several bitmaps can remain in memory without
* an outOfMemoryException error.*/
byte[] thumbnail;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(
file.toString(), options);
options.inSampleSize = calculateInSampleSize(
options, imageWidth, imageWidth);
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(file.toString(),
options);
/*now the size of the Bitmap is manageable, we set about sizing the
* thumbnail correctly, preserving the Aspect Ratio */
final int REQUIRED_SIZE = imageWidth;
int thumbHeight = REQUIRED_SIZE, thumbWidth = REQUIRED_SIZE;
float ratio = (float) bitmap.getWidth() // Work out the aspect ratio.
/ (float) bitmap.getHeight();
if (ratio == 1) {
thumbHeight = REQUIRED_SIZE;
thumbWidth = REQUIRED_SIZE;
} else if (ratio < 1) {
thumbHeight = REQUIRED_SIZE;
thumbWidth = (int) ((float) REQUIRED_SIZE * (float) ratio);
} else {
thumbWidth = REQUIRED_SIZE;
thumbHeight = (int) ((float) REQUIRED_SIZE / (float) ratio);
}
Bitmap bitmap2 = Bitmap.createScaledBitmap(
bitmap, thumbWidth, thumbHeight, false);
ByteArrayOutputStream out;
try {
out = new ByteArrayOutputStream();
bitmap2.compress(CompressFormat.JPEG, 30, out); // Compress the bitmap
thumbnail = out.toByteArray();
out.close(); // close the out stream.
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
thumbnail = new byte[1];
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
thumbnail = new byte[1];
}
return thumbnail;
}
private int calculateInSampleSize(Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if(height > reqHeight || width > reqWidth)
{
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
The main thread has the handler at the top (See code below - Just the handler code for brevity) as normal, and uses the notifyDataSetChanged() method of a custom adapter (code included)...
public class MonthActivity extends Activity {
List<PhotoData> photoList;
static List<MonthData> photos;
int imageWidth;
GridView photoGrid;
static ImageAdapter2 iAdapter2;
static int year;
Thread monthSortThread;
static Handler handler2 = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
Bundle bundle = msg.getData(); // Get the message sent to the Handler.
boolean ended = bundle.getBoolean("end");
if(ended)
{
iAdapter2.notifyDataSetChanged();
//Toast.makeText(getBaseContext(), "FINISHED !!!", Toast.LENGTH_LONG).show();
} else
{
MonthData md = new MonthData();
md.monthValue = bundle.getInt("month");
md.monthString = getMonthString(md.monthValue);
Log.d("Debug", md.monthString + " " + String.valueOf(year));
md.count = bundle.getInt("count");
byte[] tn = bundle.getByteArray("thumbnail");
md.thumbnail = BitmapFactory.decodeByteArray(tn, 0, tn.length);
photos.add(md);
iAdapter2.notifyDataSetChanged();
}
}
};
(Adapter code)
public class ImageAdapter2 extends BaseAdapter{
List<MonthData> photos;
Context context;
int year, imageWidth;
public ImageAdapter2 (Context ct, List<MonthData> pList, int yr, int i) {
photos = new ArrayList<MonthData>();
photos = pList;
context = ct;
year = yr;
imageWidth = i;
}
#Override
public int getCount() {
return photos.size();
}
#Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int arg0) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View myView = null;
if(convertView == null)
{
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myView = li.inflate(R.layout.grid_cell, null);
} else
{
myView = convertView;
}
TextView tv = (TextView)myView.findViewById(R.id.photoText);
if(year == 0)
{
int count = photos.get(position).count;
tv.setText(String.valueOf(count));
} else
{
int count = photos.get(position).count;
String month = photos.get(position).monthString;
String yearString = String.valueOf(year);
tv.setText(month + " " + yearString + " (" + String.valueOf(count) + ")");
}
ImageView iv = (ImageView)myView.findViewById(R.id.photoViewGridCell);
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
iv.setPadding(0, 0, 0, 0);
iv.setLayoutParams(new LinearLayout.LayoutParams(imageWidth, imageWidth));
iv.setMaxHeight(imageWidth);
iv.setMaxWidth(imageWidth);
iv.setImageBitmap(photos.get(position).thumbnail);
return myView;
}
}
Please note that MonthActivity is called, via an Intent, on selecting a Custom View (specifically a collection of views, in a separate xml layout file) ImageAdapter2 is just a small variation on a similar Adapter used for the "starting" activity, with a slightly different custom view.
Also, ImageAdapter2 is properly "connected" to the Layout required, and initiated in the onCreate() method, it even successfully runs the constructor, but despite having several different breakpoints within the adapter's getView method none of them are ever reached when debugging... very frustrating.. any ideas ?
Put your Runnable in runOnUiThread method when you call it.