Hello stackoverflow I'm trying to develop an android application to play my own GIF, here is the code snippet
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
AnimationView.java
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.util.AttributeSet;
import android.view.View;
public class AnimationView extends View {
private Movie mMovie;
private long mMovieStart;
private static final boolean DECODE_STREAM = true;
private int mDrawLeftPos;
private int mHeight, mWidth;
private static byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int len;
try {
while ((len = is.read(buffer)) >= 0) {
os.write(buffer, 0, len);
}
} catch (java.io.IOException e) {
}
return os.toByteArray();
}
public AnimationView(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusable(true);
java.io.InputStream is;
is = context.getResources().openRawResource(R.drawable.scanning);
if (DECODE_STREAM) {
mMovie = Movie.decodeStream(is);
} else {
byte[] array = streamToBytes(is);
mMovie = Movie.decodeByteArray(array, 0, array.length);
}
}
#Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec )
{
int p_top = this.getPaddingTop(), p_bottom = this.getPaddingBottom();
mWidth = mMovie.width();
mHeight = mMovie.height();
// Calculate new desired height
final int desiredHSpec = MeasureSpec.makeMeasureSpec( mHeight + p_top + p_bottom , MeasureSpec.EXACTLY );
setMeasuredDimension( widthMeasureSpec, desiredHSpec );
super.onMeasure( widthMeasureSpec, desiredHSpec );
// Update the draw left position
mDrawLeftPos = Math.max( ( this.getWidth() - mWidth ) / 2, 0) ;
}
#Override
public void onDraw(Canvas canvas) {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) { // first time
mMovieStart = now;
}
if (mMovie != null) {
int dur = mMovie.duration();
if (dur == 0) {
dur = 3000;
}
int relTime = (int) ((now - mMovieStart) % dur);
// Log.d("", "real time :: " +relTime);
mMovie.setTime(relTime);
mMovie.draw(canvas, mDrawLeftPos, this.getPaddingTop());
invalidate();
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical" >
<com.example.androidgifwork.AnimationView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</LinearLayout>
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidgifwork"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="com.example.androidgifwork.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
When I run the above code snippet GIF is not at all running, but when I remove android:targetSdkVersion="19" GIF running, please help me to solve this riddle.
Thanks
2017 UPDATED ANSWER
To play GIF in android use Glide library to load any image or GIF.
Glide.with(context)
.load(YOUR_GIF)
.into(YOUR_IMAGE_VIEW);
Use Glide to load normal images, images from server or even to load GIF as well. Also have a look at Picasso android image loading library which is similar to Glide but as of now(16 Apr 2017) Picasso doesn't support GIF loading in android yet.
######################################################################
OLD ANSWER
For all Those who want to play GIF in your app please find the code below
PlayGifView.java
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
public class PlayGifView extends View{
private static final int DEFAULT_MOVIEW_DURATION = 1000;
private int mMovieResourceId;
private Movie mMovie;
private long mMovieStart = 0;
private int mCurrentAnimationTime = 0;
#SuppressLint("NewApi")
public PlayGifView(Context context, AttributeSet attrs) {
super(context, attrs);
/**
* Starting from HONEYCOMB have to turn off HardWare acceleration to draw
* Movie on Canvas.
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
public void setImageResource(int mvId){
this.mMovieResourceId = mvId;
mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
requestLayout();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(mMovie != null){
setMeasuredDimension(mMovie.width(), mMovie.height());
}else{
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}
}
#Override
protected void onDraw(Canvas canvas) {
if (mMovie != null){
updateAnimtionTime();
drawGif(canvas);
invalidate();
}else{
drawGif(canvas);
}
}
private void updateAnimtionTime() {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) {
mMovieStart = now;
}
int dur = mMovie.duration();
if (dur == 0) {
dur = DEFAULT_MOVIEW_DURATION;
}
mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
}
private void drawGif(Canvas canvas) {
mMovie.setTime(mCurrentAnimationTime);
mMovie.draw(canvas, 0, 0);
canvas.restore();
}
}
In your activity class use the following code to play GIF
PlayGifView pGif = (PlayGifView) findViewById(R.id.viewGif);
pGif.setImageResource(<Your GIF file name Eg: R.drawable.infinity_gif>);
XML layout
<yourPacckageName.PlayGifView
android:id="#+id/viewGif"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
Use Glide to load any gif from your project's raw folder or external URL into ImageView, ImageButtons or similar [Glide targets][2]
Glide.with(getActivity()).load(R.raw.alarm).asGif().into(btnAlert);
If you can use a WebView, GIFs will play directly over it. But I am not sure, in your case whether you want to put it in a Webview or not.
add this to your dependencies (build.gradle)
allprojects {
repositories {
mavenCentral()
}
}
dependencies {
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15'
}
and use this in xml to show your gif
<pl.droidsonroids.gif.GifTextView
android:id="#+id/gifTextView2"
android:layout_width="230dp"
android:layout_height="200dp"
android:layout_weight="1"
android:src="#drawable/dev_option_gif"
/>
More info at: https://github.com/koral--/android-gif-drawable
Android provides the class android.graphics.Movie. This class is capable of decoding and playing InputStreams. So for this approach, we create a class GifMovieView and let it inherit from View a detailed tutorial is given at http://droid-blog.net/2011/10/14/tutorial-how-to-use-animated-gifs-in-android-part-1/
Source Code
https://drive.google.com/open?id=0BzBKpZ4nzNzUZy1BVlZSbExvYUU
** android:hardwareAccelerated="false" in Manifest File**
package com.keshav.gifimageexampleworking;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
public class MainActivity extends AppCompatActivity
{
private GifImageView gifImageView;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gifImageView = (GifImageView) findViewById(R.id.GifImageView);
gifImageView.setGifImageResource(R.drawable.success1);
}
#Override
protected void onResume()
{
super.onResume();
//refresh long-time task in background thread
new Thread(new Runnable() {
#Override
public void run() {
try {
//dummy delay for 2 second
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//update ui on UI thread
runOnUiThread(new Runnable() {
#Override
public void run() {
gifImageView.setGifImageResource(R.drawable.success);
}
});
}
}).start();
}
}
package com.keshav.gifimageexampleworking;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.net.Uri;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class GifImageView extends View {
private InputStream mInputStream;
private Movie mMovie;
private int mWidth, mHeight;
private long mStart;
private Context mContext;
public GifImageView(Context context) {
super(context);
this.mContext = context;
}
public GifImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
if (attrs.getAttributeName(1).equals("background")) {
int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
setGifImageResource(id);
}
}
private void init() {
setFocusable(true);
mMovie = Movie.decodeStream(mInputStream);
mWidth = mMovie.width();
mHeight = mMovie.height();
requestLayout();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mWidth, mHeight);
}
#Override
protected void onDraw(Canvas canvas) {
long now = SystemClock.uptimeMillis();
if (mStart == 0) {
mStart = now;
}
if (mMovie != null) {
int duration = mMovie.duration();
if (duration == 0) {
duration = 1000;
}
int relTime = (int) ((now - mStart) % duration);
mMovie.setTime(relTime);
mMovie.draw(canvas, 10, 10);
invalidate();
}
}
public void setGifImageResource(int id) {
mInputStream = mContext.getResources().openRawResource(id);
init();
}
public void setGifImageUri(Uri uri) {
try {
mInputStream = mContext.getContentResolver().openInputStream(uri);
init();
} catch (FileNotFoundException e) {
Log.e("GIfImageView", "File not found");
}
}
}
For devices running API 29(Pie) & above you can use AnimatedImageDrawable to play GIF(s) and WebP animated images.
Here's a sample:
ImageView imgGif = findViewById(R.id.imgGif);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
drawable = ImageDecoder.decodeDrawable(ImageDecoder.createSource(getResources(), R.drawable.giphy));
imgGif.setImageDrawable(drawable);
if (drawable instanceof AnimatedImageDrawable) {
((AnimatedImageDrawable) drawable).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Using this provides more control & features you can use on the image such as:
Checking whether the gif is running
Starting & stopping the playback.
Applying color filters
Changing image shape
and much more
You can also use Coil library, which gives you a bit less control over Gif images than Glide does. But still gives simple implementation if you just want to load the GIF once and show. Probably, if you want to repeat that you need to trigger the drawable again or just call the load function once again. Anyway even if calling load again the resources should be cached under the hood(something to be investigated, not 100% sure).
Everything you need is adding dependency:
implementation "io.coil-kt:coil-gif:1.0.0"
And specifying your decoding mechanism:
val imageLoader = ImageLoader.Builder(context)
.componentRegistry {
if (SDK_INT >= 28) {
add(ImageDecoderDecoder())
} else {
add(GifDecoder())
}
}
.build()
That's all go-ahead and load images/gifs directly in your image views:
app_logo.load("needed_gif_url")
Don't forget to pass the needed image loader to each request where you need your decoder to be used, as for GIFs. Or just make your custom imageLoader as default for each request:
Coil.setImageLoader(imageLoader)
Animated GIFs in Android is a difficult topic. It is an issue that has been discussed heavily and still, it is very difficult for developers to bring GIFs to life. using glide and pl.droidsonroids.gif:android-gif-drawable made some problem to build gradle. so I found a method that not using any dependencies.I used this method to load the gif in my views:
GifAnimView.java:
package com.app.samplegif;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.graphics.Paint;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;
import com.app.samplegif.R;
import java.io.IOException;
import java.io.InputStream;
#SuppressLint("NewApi")
public class GifAnimView extends View {
private Context _context;
private Movie gifMovie;
private String gifName = "not set";
private InputStream gifStream;
private int width;
private int height;
private long startTime;
private Paint paint;
public GifAnimView(Context context, AttributeSet attrs) {
super(context,attrs);
initGif(context,attrs);
_context = context;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (gifStream == null || gifMovie == null) {
canvas.drawColor(Color.WHITE);
canvas.drawText(gifName,width/2, height /2,paint);
return;
}
long now = SystemClock.uptimeMillis();
int relTime = (int) ((now - startTime) % gifMovie.duration());
gifMovie.setTime(relTime);
gifMovie.draw(canvas, 0, 0);
this.invalidate();
}
private void initGif(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.GifAnimView, 0, 0);
gifName = a.getString(R.styleable.GifAnimView_gifSrc);
try {
gifStream =context.getAssets().open(gifName);
gifMovie = Movie.decodeStream(gifStream);
startTime = SystemClock.uptimeMillis();
} catch (IOException e) {
e.printStackTrace();
}
paint = new Paint();
paint.setTextSize(40);
paint.setTextAlign(Paint.Align.CENTER);
paint.setStyle(Paint.Style.FILL);
a.recycle();
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
this.width = w;
this.height = h;
super.onSizeChanged(w, h, oldw, oldh);
}
}
create attrs.xml in res/values folder:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="GifAnimView">
<attr name="gifSrc" format="string" localization="suggested" />
</declare-styleable>
</resources>
copy your.gif file into src/main/assets/ folder
activity_main.xml:
you can use app:gifSrc attribute as name of your gif that exist in assets folder.
// ....
<com.app.samplegif.GifAnimView
android:id="#+id/gif_view"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginLeft="30dp"
android:layout_marginTop="20dp"
app:gifSrc="your.gif" />
// ....
Related
This question already has answers here:
How to draw a vertical line in "android Horizontal Progress bar"
(3 answers)
Closed 2 years ago.
While developing Android, there was a task that required to put a dividing line in the Progress Bar as shown in the following figure.
But the Progress Bar I implemented is as follows.
The progress bar I made consists of the following code.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background">
<shape android:shape="rectangle">
<solid android:color="#D1D1D6" />
<size android:height="20dp" />
<corners android:radius="12dp" />
</shape>
</item>
<item android:id="#android:id/progress">
<clip>
<shape android:shape="rectangle">
<solid android:color="#26B477" />
<size android:height="20dp" />
<corners android:radius="12dp" />
</shape>
</clip>
</item>
</layer-list>
The custom_seekbar.xml in the drawable file implemented as above was used as follows.
<ProgressBar
style="#style/Widget.AppCompat.ProgressBar.Horizontal"
android:id="#+id/todayGage"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="12dp"
android:layout_marginLeft="12dp"
android:layout_marginEnd="25dp"
android:layout_marginRight="25dp"
android:max="100"
android:paddingStart="0dp"
android:paddingLeft="0dp"
android:paddingEnd="0dp"
android:paddingRight="0dp"
android:progressDrawable="#drawable/custom_seekbar"
app:layout_constraintBottom_toBottomOf="#id/todayPercent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="#id/todayPercent"
app:layout_constraintTop_toTopOf="#id/todayPercent" />
Is there a way to put a dividing line inside the Progress Bar like I want??
Hello hanjiman
I will suggest you with two example
First One
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.widget.ProgressBar;
public class MainLayout extends Activity {
int progress=1;
ProgressBar progressBar;
#SuppressLint("ResourceType")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar= (ProgressBar)findViewById(R.id.progress_bar_test);
SeperatedProgressbar bgProgress= new SeperatedProgressbar(ContextCompat.getColor(this,R.color.color_progress),ContextCompat.getColor(this,R.color.color_normal),this);
progressBar.setProgressDrawable(bgProgress);
progressBar.setMax(100);
progressBar.setProgress(50);
new CountDownTimer(100000, 1000) {
public void onTick(long millisUntilFinished) {
progress=progress+1;
progressBar.setProgress(progress);
}
public void onFinish() {
}
}.start();
}
}
activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:paddingLeft="10dp"
android:layout_height="match_parent"
>
<ProgressBar
android:id="#+id/progress_bar_test"
android:layout_width="match_parent"
android:layout_height="25dp"
android:layout_centerInParent="true"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
android:progress="10"
/>
</RelativeLayout>
SeperatedProgressbar.java
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
public class SeperatedProgressbar extends Drawable {
private static final int NUM_SEGMENTS = 8;
private int mForeground;
private int mBackground;
private final Paint mPaint = new Paint();
private final RectF mSegment = new RectF();
Context context;
public SeperatedProgressbar(int fgColor, int bgColor, Context context) {
mForeground = fgColor;
this.context=context;
mBackground = bgColor;
}
#Override
protected boolean onLevelChange(int level) {
invalidateSelf();
return true;
}
#Override
public void draw(Canvas canvas) {
float level = getLevel() / 1000f;
Rect b = getBounds();
float gapWidth = b.height() / 8f;
float segmentWidth = (b.width() - (NUM_SEGMENTS - 1) * gapWidth) / NUM_SEGMENTS;
mSegment.set(0, 0, segmentWidth, b.height());
mPaint.setColor(mForeground);
for (int i = 0; i < NUM_SEGMENTS; i++) {
float loLevel = i / (float) NUM_SEGMENTS;
float hiLevel = (i + 1) / (float) NUM_SEGMENTS;
if (loLevel <= level && level <= hiLevel) {
float middle = mSegment.left + NUM_SEGMENTS * segmentWidth * (level - loLevel);
canvas.drawRect(mSegment.left, mSegment.top, middle, mSegment.bottom, mPaint);
mPaint.setColor(mBackground);
canvas.drawRect(middle, mSegment.top, mSegment.right, mSegment.bottom, mPaint);
} else {
canvas.drawRect(mSegment, mPaint);
}
mSegment.offset(mSegment.width() + gapWidth, 0);
}
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Result
Second Answer
MainActivity.Java
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import com.rachitgoyal.segmented.SegmentedProgressBar;
import java.util.ArrayList;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
SegmentedProgressBar mSegmentedProgressBar;
ArrayList<Integer> arrayList=new ArrayList<>();;
int progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSegmentedProgressBar = findViewById(R.id.segmented_pb_1);
arrayList.add(progress);
new CountDownTimer(200000, 2000) {
public void onTick(long millisUntilFinished) {
if(progress==0)
{
arrayList.add(progress);
}
else
{
arrayList.add(progress);
}
progress=progress+1;
mSegmentedProgressBar.setEnabledDivisions(arrayList);
}
public void onFinish() {
}
}.start();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.test.package.SegmentedProgressBar
android:id="#+id/segmented_pb_1"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_centerHorizontal="true"
android:layout_margin="10dp"
android:layout_centerInParent="true"
app:cornerRadius="20dp"
app:dividerColor="#color/white"
app:dividerWidth="2dp"
app:divisions="10"
app:isDividerEnabled="true"
app:progressBarBackgroundColor="#dadada"
app:progressBarColor="#ff2d2d" />
</RelativeLayout>
SegmentedProgressBar.java
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import com.test.package.segmentedprogressbar.R;
import java.util.ArrayList;
import java.util.List;
public class SegmentedProgressBar extends View {
private static final String TAG = "SegmentedProgressBar";
RectF bgRect;
private Paint progressBarBackgroundPaint = new Paint();
private Paint progressBarPaint = new Paint();
private Paint dividerPaint = new Paint();
private int progressBarWidth;
private float percentCompleted;
private float dividerWidth = 1;
private boolean isDividerEnabled = true;
private int divisions = 1;
private List<Integer> enabledDivisions = new ArrayList<>();
private List<Float> dividerPositions;
private float cornerRadius = 20f;
public SegmentedProgressBar(Context context) {
super(context);
init(null);
}
private void init(AttributeSet attrs) {
dividerPositions = new ArrayList<>();
cornerRadius = 0;
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.SegmentedProgressBar, 0, 0);
try {
dividerPaint.setColor(typedArray.getColor(R.styleable.SegmentedProgressBar_dividerColor,
ContextCompat.getColor(getContext(), R.color.white)));
progressBarBackgroundPaint.setColor(typedArray.getColor(R.styleable.SegmentedProgressBar_progressBarBackgroundColor,
ContextCompat.getColor(getContext(), R.color.grey_light)));
progressBarPaint.setColor(typedArray.getColor(R.styleable.SegmentedProgressBar_progressBarColor,
ContextCompat.getColor(getContext(), R.color.progress_bar)));
dividerWidth = typedArray.getDimension(R.styleable.SegmentedProgressBar_dividerWidth, dividerWidth);
isDividerEnabled = typedArray.getBoolean(R.styleable.SegmentedProgressBar_isDividerEnabled, true);
divisions = typedArray.getInteger(R.styleable.SegmentedProgressBar_divisions, divisions);
cornerRadius = typedArray.getDimension(R.styleable.SegmentedProgressBar_cornerRadius, 2f);
} finally {
typedArray.recycle();
}
ViewTreeObserver viewTreeObserver = getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (getWidth() > 0) {
getViewTreeObserver().removeOnGlobalLayoutListener(this);
progressBarWidth = getWidth();
dividerPositions.clear();
if (divisions > 1) {
for (int i = 1; i < divisions; i++) {
dividerPositions.add(((float) (progressBarWidth * i) / divisions));
}
}
bgRect = new RectF(0, 0, getWidth(), getHeight());
updateProgress();
}
}
});
}
}
/**
* Updates the progress bar based on manually passed percent value.
*/
private void updateProgress() {
invalidate();
}
public SegmentedProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public SegmentedProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SegmentedProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (bgRect != null) {
canvas.drawRoundRect(bgRect, cornerRadius, cornerRadius, progressBarBackgroundPaint);
for (Integer enabledDivision : enabledDivisions) {
if (enabledDivision < divisions) {
float left = 0;
if (enabledDivision != 0) {
left = dividerPositions.get(enabledDivision - 1) + dividerWidth;
}
float right = enabledDivision >= dividerPositions.size() ? progressBarWidth : dividerPositions.get(enabledDivision);
RectF rect = new RectF(left, 0, right, getHeight());
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, progressBarPaint);
if (enabledDivision == 0) {
canvas.drawRect(left + cornerRadius, 0, right, getHeight(), progressBarPaint);
} else if (enabledDivision == divisions - 1) {
canvas.drawRect(left, 0, right - cornerRadius, getHeight(), progressBarPaint);
} else {
canvas.drawRect(rect, progressBarPaint);
}
}
if (divisions > 1 && isDividerEnabled) {
for (int i = 1; i < divisions; i++) {
float leftPosition = dividerPositions.get(i - 1);
canvas.drawRect(leftPosition, 0, leftPosition + dividerWidth, getHeight(), dividerPaint);
}
}
}
}
}
/**
* Set the color for the progress bar background
*
* #param color
*/
public void setBackgroundColor(int color) {
progressBarBackgroundPaint.setColor(color);
}
public void reset() {
dividerPositions.clear();
percentCompleted = 0;
updateProgress();
}
/**
* Set the color for the progress bar
*
* #param color
*/
public void setProgressBarColor(int color) {
progressBarPaint.setColor(color);
}
/**
* Set the color for the divider bar
*
* #param color
*/
public void setDividerColor(int color) {
dividerPaint.setColor(color);
}
/**
* set the width of the divider
*
* #param width
*/
public void setDividerWidth(float width) {
if (width < 0) {
Log.w(TAG, "setDividerWidth: Divider width can not be negative");
return;
}
dividerWidth = width;
}
/**
* Set whether the dividers are enabled or not.
*
* #param value true or false
*/
public void setDividerEnabled(boolean value) {
isDividerEnabled = value;
}
/**
* Sets the number of divisions in the ProgressBar.
*
* #param divisions number of divisions
*/
public void setDivisions(int divisions) {
if (divisions < 1) {
Log.w(TAG, "setDivisions: Number of Divisions cannot be less than 1");
return;
}
this.divisions = divisions;
dividerPositions.clear();
if (divisions > 1) {
for (int i = 1; i < divisions; i++) {
dividerPositions.add(((float) (progressBarWidth * i) / divisions));
}
}
updateProgress();
}
/**
* Set the enabled divisions to specified value.
*
* #param enabledDivisions number of divisions to be enabled
*/
public void setEnabledDivisions(List<Integer> enabledDivisions) {
this.enabledDivisions = enabledDivisions;
updateProgress();
}
public void setCornerRadius(float cornerRadius) {
this.cornerRadius = cornerRadius;
}
}
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SegmentedProgressBar">
<attr name="dividerColor" format="color" />
<attr name="progressBarBackgroundColor" format="color" />
<attr name="progressBarColor" format="color" />
<attr name="dividerWidth" format="dimension" />
<attr name="isDividerEnabled" format="boolean" />
<attr name="divisions" format="integer" />
<attr name="cornerRadius" format="dimension" />
</declare-styleable>
</resources>
response
My issue is that no matter what I do, no matter how many questions and answers I read through on the internet, I cant get a simple rectangle to draw on my android device screen. Let me rephrase that, it shows up on screen but it wont change. I cant get an animation to update. onDraw() never calls multiple times, just once on startup. why? Here is my view objects code:
package prospect_industries.es;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class TestView extends View {
//Variables
public static final int SIZE = 300;
public float TOP = 0.0f;
public float LEFT = 0.0f;
public float RIGHT = 100f;
public float BOTTOM = 100f;
private Paint rectanglePaint;
private RectF rect1;
//Constructors
public TestView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
init();
}
public TestView(final Context context, final AttributeSet attrs) {
super(context, attrs, 0);
init();
}
public TestView(final Context context) {
super(context, null, 0);
init();
}
//View methods
#Override
protected void onDraw(final Canvas canvas){
canvas.drawRect(rect1, rectanglePaint);
Log.i("test1", "in onDraw");
}
#Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
final int chosenWidth = chooseDimension(widthMode, widthSize);
final int chosenHeight = chooseDimension(heightMode, heightSize);
setMeasuredDimension(chosenWidth, chosenHeight);
Log.i("test1", String.valueOf(chosenWidth));
Log.i("test1",String.valueOf(chosenHeight));
}
//Class Methods
private int chooseDimension(final int mode, final int size) {
switch (mode) {
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
return size;
case MeasureSpec.UNSPECIFIED:
default:
return getDefaultDimension();
}
}
private int getDefaultDimension() { return SIZE; }
private void init(){
requestFocus();
rectanglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
rectanglePaint.setColor(-1);
rectanglePaint.setStyle(Paint.Style.FILL);
rect1 = new RectF(LEFT, TOP, RIGHT, BOTTOM);
}
public void update() {
RIGHT += 10;
BOTTOM += 10;
rect1 = new RectF(LEFT, TOP, RIGHT, BOTTOM);
invalidate();
Log.i("test1", "in update");
}
}
Here is my main class which has a few methods for other things Im working on as well as a timer which calls the update() method inside of my test view object.
package prospect_industries.es;
import android.app.Activity;
import android.content.Context;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends Activity {
private boolean setup = false;
public int waitDelay = 1000; //Milliseconds - currently 1 second
private Timer checkTime;
private TimerTask listen;
private MediaRecorder mRecorder;
//Splashscreen
private Timer splashScreen;
private int waitTime = 3000; //3 seconds
private GaugeView mGaugeView;
private final Random RAND = new Random();
private TestView testview;
private SurfaceHolder surfaceHolder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
//mGaugeView = (GaugeView) findViewById(R.id.gauge_view);
testview = (TestView) findViewById(R.id.test_view);
}
#Override
public void onStart() {
super.onStart();
//Timers
//1 second wait tick
checkTime = new Timer();
checkTime.schedule(new TimerTask() {
public void run() {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
//mGaugeView.setTargetValue(RAND.nextInt(101));
testview.update();
}
});
}
}, 0, waitDelay);
//Set splash screen wait timer
splashScreen = new Timer();
splashScreen.schedule(new TimerTask() {
public void run() {
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
setContentView(R.layout.content_main);
}
});
splashScreen.cancel();
}
}, waitTime);
//set welcome screen
setContentView(R.layout.activity_welcome);
}
#Override
public void onStop() {
super.onStop();
if(checkTime != null) {
checkTime.cancel();
stop();
}
}
public void stop() {
if (mRecorder != null) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
}
public double getAmplitude() {
if (mRecorder != null)
return mRecorder.getMaxAmplitude();
else
return 0;
}
public void checkSound(){
if (mRecorder == null) {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mRecorder.setOutputFile("/dev/null");
try {
mRecorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
mRecorder.start();
}
}
private static class MainView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder surfaceHolder;
public MainView(Context context) {
super(context);
surfaceHolder = getHolder();
surfaceHolder.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
}
Lastly, here is the xml layout file which loads in the test view object.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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"
android:background="#103681"
tools:context="prospect_industries.es.MainActivity">
<prospect_industries.es.TestView
android:layout_width="200dp"
android:layout_height="200dp"
android:id="#+id/test_view"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
I have been looking all over stackExchange for hours but I cant fix my problem, onDraw is only called once and never again no matter what I do. Right now the rectangle should be expanding out but it isnt being redrawn.
The problem is that you initialize the rectangle to be 1px wide and 1px tall and never resize it. You should be able to see 1 white pixel somewhere in the top left corner of your TestView.
Try changing the rect1 size to 0,0,100,100 and see if the problem persists.
public static final float TOP = 0.0f;
public static final float LEFT = 0.0f;
public static final float RIGHT = 100.0f;
public static final float BOTTOM = 100.0f;
I have a working frame-by-frame animation inside an Android application.
That's fine - but how would I add a scrolling background to this application, so that the animation run over the background?
I can find plenty of examples for implementing the 2 processes separately - but NOT together.
Any help would be appreciated!
Here's my code for the animation Activity..
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceView;
import android.widget.ImageView;
/**
*
*
*/
public class MyAnimationView extends ImageView
{
private static final String TAG = "AnimationTest:AnimationView";
private Context mContext = null;
private static final int DELAY = 100; //delay between frames in milliseconds
private boolean mIsPlaying = false;
private boolean mStartPlaying = false;
private ArrayList<Bitmap> mBitmapList = new ArrayList<Bitmap>();
private int play_frame = 0;
private long last_tick = 0;
public MyAnimationView(Context context, AttributeSet attrs)
{
super(context, attrs);
mContext = context;
}
#Override
protected void onDraw(Canvas c)
{
Log.d(TAG, "onDraw called");
if (mStartPlaying)
{
Log.d(TAG, "starting animation...");
play_frame = 0;
mStartPlaying = false;
mIsPlaying = true;
postInvalidate();
}
else if (mIsPlaying)
{
if (play_frame >= mBitmapList.size())
{
mIsPlaying = false;
}
else
{
long time = (System.currentTimeMillis() - last_tick);
int draw_x = 0;
int draw_y = 0;
if (time >= DELAY) //the delay time has passed. set next frame
{
last_tick = System.currentTimeMillis();
c.drawBitmap(mBitmapList.get(play_frame), draw_x, draw_y, null);
play_frame++;
postInvalidate();
}
else //still within delay. redraw current frame
{
c.drawBitmap(mBitmapList.get(play_frame), draw_x, draw_y, null);
postInvalidate();
}
}
}
}
/*ideally this should be in a background thread*/
public void loadAnimation(String prefix, int nframes)
{
mBitmapList.clear();
for (int x = 0; x < nframes; x++)
{
String name = prefix + "_" + x;
Log.d(TAG, "loading animation frame: " + name);
int res_id = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
BitmapDrawable d = (BitmapDrawable) mContext.getResources().getDrawable(res_id);
mBitmapList.add(d.getBitmap());
}
}
public void playAnimation()
{
mStartPlaying = true;
postInvalidate();
}
}
"draw_x" and "draw_y" are the variables for your bitmap position. If you change this two variables (or just one of them), you can move (scroll) your picture over the screen.
Is there a way to for a Bitmap object to have a transparent background instead of solid color.. I'd like my background image (set on XML layout to show)?
sadly, none of the other question worked for me, otherwise, I wouldn't be asking... THANKS IN ADVANCE! ALSO, any tips on how to get the animation to work with XML?
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.View;
public class Vehicle extends View{
Bitmap vehicle;
int x_axisMovement;
public Vehicle(Context context) {
super(context);
// TODO Auto-generated constructor stub
vehicle = BitmapFactory.decodeResource(getResources(), R.drawable.vehicle_bus);
x_axisMovement = 1024;
}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT); // NOT WORKING
canvas.drawBitmap(vehicle, x_axisMovement, 400, null);
if(x_axisMovement > -256){
x_axisMovement -= 4;
}
else
{
x_axisMovement = 1024;
}
invalidate();
}
}
You can use it in xml and add it in a relative layout so youll have a view on top of another view
sample:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/ic_launcher" >
<com.example.Vehicle
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Now com.example is the folder inside your src folder but you might have different folder just press ctrl+shift+o so it will automatically give you an hint to the class location.
Now your class
public class Vehicle extends View {
Bitmap vehicle;
int x_axisMovement;
public Vehicle(Context context) {
super(context);
init();
}
public Vehicle(Context context, AttributeSet s) {
super(context, s);
init();
}
public Vehicle(Context context, AttributeSet s, int style) {
super(context, s, style);
init();
}
public void init() {
vehicle = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
x_axisMovement = 1024;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT); // NOT WORKING
canvas.drawBitmap(vehicle, x_axisMovement, 400, null);
if (x_axisMovement > -256) {
x_axisMovement -= 4;
} else {
x_axisMovement = 1024;
}
invalidate();
}
}
You need to have that 3 constructor to enable the class to be used in an xml form.
now the result:
The background is the big ic_launcher and the image from your class in the small ic_launcher.
I'm following the Android Game Programming for Dummies by: Derek James. It looks outdated too me because in the book it doesn't include the fragment_main. So I just copied the fragment_main and replaced it in the activity_main and deleted the fragment_main (I followed the how to get rid of fragment_main tutorial on this forum).
I'm trying to make the whack a mole game in the book.
Anyways I get two errors when I followed the book and it said I can run the program now but I am unable to do that because of:
background cannot be resolved or is not a field
title cannot be resolved or is not a field
I have everything the same as the book but why do I get these errors, I checked all over Google to find an answer or something similar but I can't find the error, I would really appreciate if someone can help me out. Sorry for writing a whole paragraph but I should let you know what I did.
This is my WhackAMoleView.java
package com.whackamole;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class WhackAMoleView extends SurfaceView implements
SurfaceHolder.Callback {
private Context myContext;
private SurfaceHolder mySurfaceHolder;
private Bitmap backgroundImg;
private int screenW = 1;
private int screenH = 1;
private boolean running = false;
private boolean onTitle = true;
private WhackAMoleThread thread;
public WhackAMoleView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
thread = new WhackAMoleThread(holder, context, new Handler()
{
#Override
public void handleMessage(Message m) {
}
});
setFocusable(true);
}
public WhackAMoleThread getThread() {
return thread;
}
class WhackAMoleThread extends Thread {
public WhackAMoleThread(SurfaceHolder surfaceHolder, Context context,
Handler handler) {
mySurfaceHolder = surfaceHolder;
myContext = context;
backgroundImg = BitmapFactory.decodeResource(
context.getResources(), R.drawable.title);
}
#Override
public void run() {
while (running) {
Canvas c = null;
try {
c = mySurfaceHolder.lockCanvas(null);
synchronized (mySurfaceHolder) {
draw(c);
}
} finally {
if (c != null) {
mySurfaceHolderunlockCanvasAndPost(c);
}
}
}
}
private void mySurfaceHolderunlockCanvasAndPost(Canvas c) {
// TODO Auto-generated method stub
}
private void draw(Canvas canvas) {
try {
canvas.drawBitmap(backgroundImg, 0, 0, null);
} catch (Exception e) {
}
}
boolean doTouchEvent(MotionEvent event) {
synchronized (mySurfaceHolder) {
int eventaction = event.getAction();
int x = (int) event.getX();
int Y = (int) event.getY();
switch (eventaction) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (onTitle) {
backgroundImg = BitmapFactory
.decodeResource(myContext.getResources(),
R.drawable.background);
backgroundImg = Bitmap.createScaledBitmap(
backgroundImg, screenW, screenH, true);
onTitle = false;
}
break;
}
}
return true;
}
public void setSurfaceSize(int width, int height) {
synchronized (mySurfaceHolder) {
screenW = width;
screenH = height;
backgroundImg = Bitmap.createScaledBitmap(backgroundImg, width,
height, true);
}
}
public void setRunning(boolean b) {
running = b;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return thread.doTouchEvent(event);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
thread.setSurfaceSize(width, height);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
if (thread.getState() == Thread.State.NEW) {
thread.start();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
thread.setRunning(false);
}
}
This is my MainActivity.java
package com.whackamole;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
private WhackAMoleView myWhackAMoleView;
/**Called when the activity is first created. */
#Override
public 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.whackamole_layout);
myWhackAMoleView = (WhackAMoleView)
findViewById(R.id.mole);
myWhackAMoleView.setKeepScreenOn(true);
}
}
This is my AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.whackamole"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name">
<activity
android:name="com.whackamole.MainActivity"
android:screenOrientation="landscape"
android:configChanges="keyboardHidden|orientation"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
mySurfaceHolderunlockCanvasAndPost(c);
}
}
}
}
mySurfaceHolderunlockCanvasAndPost should be mySurfaceHolder.unlockCanvasAndPost
*private void mySurfaceHolderunlockCanvasAndPost(Canvas c) {
// TODO Auto-generated method stub
}* should not be here