I have a TextView whose layout_width is set to wrap_content . I use a nine-patch image as background. TextView's content is periodically refreshed (I use thread which sleeps some time and then starts a handler). After few iterations there are some artifacts. I can see a part of text and background of some earlier message (it's width is bigger then the width of currently shown message). What could be the problem?
public void onCreate{
...
helpField = (TextView) findViewById(R.id.helpField);
...
}
private class PeriodicHelp extends Thread{
private static final int SLEEP_TIME = 4000;
private static final int NUM_HELP_PHRASES = 5;
private String getHelp(){
int pos = randomGenerator.nextInt(NUM_HELP_PHRASES);
return helpPhrases[pos];
}
public void run(){
int i = 0;
while(true){
try {
Thread.sleep(SLEEP_TIME);
Log.d(TAG, "periodicHelp " + Integer.toString(i));
i++; //used just for debugging
mHandler.post(new Runnable(){
public void run() {
String help = getHelp();
helpField.setText(help);
}
});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
I start my PeriodicHelp thread in onStart()
I manged to solve this problem. Here is the code
public void onCreate{
...
helpField = (TextView) findViewById(R.id.helpField);
rl = (RelativeLayout) findViewById(R.id.mainView); //this RelativeLayout contains
//helpField TextView
...
}
private class PeriodicHelp extends Thread{
private static final int SLEEP_TIME = 4000;
private static final int NUM_HELP_PHRASES = 5;
private String getHelp(){
int pos = randomGenerator.nextInt(NUM_HELP_PHRASES);
return helpPhrases[pos];
}
public void run(){
int i = 0;
while(true){
try {
Thread.sleep(SLEEP_TIME);
Log.d(TAG, "periodicHelp " + Integer.toString(i));
i++; //used just for debugging
rl.postInvalidate();//THIS LINE FIX THE PROBLEM
mHandler.post(new Runnable(){
public void run() {
String help = getHelp();
helpField.setText(help);
}
});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
Related
I redesigned my project by simplifying the code programming. i placed all the images that are static through the xml layout.
i get only 2 results when i run the program.
1) running with no problems
2) running with problems
i get the following error:
Connected to process 10651 on device 4.7_WXGA_API_22 [emulator-5554]
I/art: Not late-enabling -Xcheck:jni (already on)
W/art: Before Android 4.1, method android.graphics.PorterDuffColorFilter android.support.graphics.drawable.VectorDrawableCompat.updateTintFilter(android.graphics.PorterDuffColorFilter, android.content.res.ColorStateList, android.graphics.PorterDuff$Mode) would have incorrectly overridden the package-private method in android.graphics.drawable.Drawable
I believe the problem lies somewhere in this inner class, i think?!
private class MatchCardGame{
private Game mMatchGame;
private List<Drawable> revealImagesOfCards;
private List<Integer> revealCards;
private Drawable hiddenCard;
private List<Integer> cardPoints;
private List< Boolean> isHidden;
public MatchCardGame(int numOfCards){
mMatchGame = new Game(numOfCards);
revealImagesOfCards = new ArrayList<>();
revealCards = new ArrayList<>();
cardPoints = new ArrayList<>();
isHidden = new ArrayList<>();
setCoverCard();
for(int i = 1; i <= numOfCards; i++)
setMatchImageCard(i);
}
public void setMatchImageCard( int cardLoc){
int drawableLoc = mMatchGame.findImageOfCard(cardLoc);
Drawable drawable = ResourcesCompat.getDrawable(getResources(), drawableLoc, null);
Integer revealCard = mMatchGame.findContentsOfCard(cardLoc);
revealImagesOfCards.add(drawable);
revealCards.add(revealCard);
cardPoints.add(Integer.valueOf(20));
Boolean hideCard = true;
isHidden.add(hideCard);
}
private void setCoverCard(){
hiddenCard = ResourcesCompat.getDrawable(getResources(), R.drawable.black_card, null);
}
public Drawable getImage(int loc, boolean statReveal){
loc--;
if(!statReveal){
Boolean hideCard = isHidden.get(loc);
hideCard = true;
return hiddenCard;
}
else {
Boolean hideCard = isHidden.get(loc);
hideCard = false;
return revealImagesOfCards.get(loc);
}
}
public boolean getHiddenStat(int loc){
loc--;
Boolean hideCard = isHidden.get(loc);
return hideCard;
}
public boolean compareCards(int loc1, int loc2){
loc1--;
loc2--;
Integer card1 = revealCards.get(loc1);
Integer card2 = revealCards.get(loc2);
Integer cardPts1 = cardPoints.get(loc1);
Integer cardPts2 = cardPoints.get(loc2);
if(card1 == card2){
int num = Integer.valueOf( scoreText.getText().toString());
Log.i("TAGG","Score Points: " + (cardPts1 + cardPts2));
new AdjustScore().execute(Integer.valueOf(cardPts1 + cardPts2));
return true;
}
else{
cardPts1 -= 5;
cardPts2 -= 5;
if(cardPts1 < 0)
cardPts1 = 0;
if(cardPts2 < 0)
cardPts2 = 0;
cardPoints.set(loc1, cardPts1);
cardPoints.set(loc2,cardPts2);
return false;
}
}
private class AdjustScore extends AsyncTask<Integer,Integer,Void>{
private TextView scoreText;
private int currentScore;
#Override
protected void onPreExecute() {
super.onPreExecute();
scoreText = (TextView) findViewById(R.id.score_txt);
currentScore = Integer.valueOf( scoreText.getText().toString());
}
#Override
protected Void doInBackground(Integer... integers) {
final int num = integers[0];
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int x = 1; x <= num; x++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(Integer.valueOf(currentScore + x));
}
}
};
new Thread(runnable).start();
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
Message msg = scoreHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.putString("myPoints",String.valueOf(values[0]));
msg.setData(bundle);
scoreHandler.sendMessage(msg);
}
}
}
I call this in onCreate of activity
private MatchCardGame myGame;
private List<Integer> selectCards;
private TextView scoreText;
private Handler scoreHandler = new Handler(){
#Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
String stat1 = bundle.getString("myPoints");
int num = Integer.valueOf(stat1);
scoreText.setText(String.valueOf(num));
}
};
#Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card_game);
myGame = new MatchCardGame(12);
}
I have another inner class derived from AsynTask.
it is mainly used to setup the card views and set clicklisteners on imageviews. but i don't think it is the problem.
First off, those aren't errors. They're the garbage collector. That's perfectly normal, and shouldn't give you any concern unless you have a performance issue at the same time. Doing something in code rather than in xml to avoid those is unnecessary, and will work or not based on pure luck of when the garbage collector is needed.
Secondly, you're loading all your images in an AsyncTask. Sometimes thats good (it stops you from pausing the main thread to load images), but if you don't have a default image in place, then until that task is done it won't actually be able to display any images. So you have a race condition between drawing and the task finishing.
Solution: do it in xml, or put up a loading screen. I suggest the first, because your task isn't actually doing anything useful if the images are static images from the app- those were loaded when the app launched.
I have custom ImageView for animated GIF image. i want to show GIF image, I tried but in this case it is contain url in Async instead I want to show GIF image from raw folder without using Glide. Anyone have any idea how to show image? Please guyz help to solve this problem!!!
I tried this for set raw file
new GifStaticData() {
#Override
protected void onPostExecute(Resource drawable) {
super.onPostExecute(drawable);
gifImageView.setImageResource(R.raw.earth_tilt_animation);
// Log.d(TAG, "GIF width is " + gifImageView.getGifWidth());
// Log.d(TAG, "GIF height is " + gifImageView.getGifHeight());
}
}.execute(R.raw.earth_tilt_animation);
GifStaticData.java
public class GifStaticData extends AsyncTask<Resource, Void, Resource> {
private static final String TAG = "GifDataDownloader";
#Override protected Resource doInBackground(final Resource... params) {
final Resource gifUrl = params[0];
if (gifUrl == null)
return null;
try {
// return ByteArrayHttpClient.get(gifUrl);
return gifUrl;
} catch (OutOfMemoryError e) {
Log.e(TAG, "GifDecode OOM: " + gifUrl, e);
return null;
}
}
}
GifImageView.java
public class GifImageView extends ImageView implements Runnable {
private static final String TAG = "GifDecoderView";
private GifDecoder gifDecoder;
private Bitmap tmpBitmap;
private final Handler handler = new Handler(Looper.getMainLooper());
private boolean animating;
private boolean shouldClear;
private Thread animationThread;
private OnFrameAvailable frameCallback = null;
private long framesDisplayDuration = -1L;
private OnAnimationStop animationStopCallback = null;
private final Runnable updateResults = new Runnable() {
#Override
public void run() {
if (tmpBitmap != null && !tmpBitmap.isRecycled()) {
setImageBitmap(tmpBitmap);
}
}
};
private final Runnable cleanupRunnable = new Runnable() {
#Override
public void run() {
tmpBitmap = null;
gifDecoder = null;
animationThread = null;
shouldClear = false;
}
};
public GifImageView(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
public GifImageView(final Context context) {
super(context);
}
public void setBytes(final byte[] bytes) {
gifDecoder = new GifDecoder();
try {
gifDecoder.read(bytes);
gifDecoder.advance();
} catch (final OutOfMemoryError e) {
gifDecoder = null;
Log.e(TAG, e.getMessage(), e);
return;
}
if (canStart()) {
animationThread = new Thread(this);
animationThread.start();
}
}
public long getFramesDisplayDuration() {
return framesDisplayDuration;
}
/**
* Sets custom display duration in milliseconds for the all frames. Should be called before {#link
* #startAnimation()}
*
* #param framesDisplayDuration Duration in milliseconds. Default value = -1, this property will
* be ignored and default delay from gif file will be used.
*/
public void setFramesDisplayDuration(long framesDisplayDuration) {
this.framesDisplayDuration = framesDisplayDuration;
}
public void startAnimation() {
animating = true;
if (canStart()) {
animationThread = new Thread(this);
animationThread.start();
}
}
public boolean isAnimating() {
return animating;
}
public void stopAnimation() {
animating = false;
if (animationThread != null) {
animationThread.interrupt();
animationThread = null;
}
}
public void clear() {
animating = false;
shouldClear = true;
stopAnimation();
handler.post(cleanupRunnable);
}
private boolean canStart() {
return animating && gifDecoder != null && animationThread == null;
}
public int getGifWidth() {
return gifDecoder.getWidth();
}
public int getGifHeight() {
return gifDecoder.getHeight();
}
#Override public void run() {
if (shouldClear) {
handler.post(cleanupRunnable);
return;
}
final int n = gifDecoder.getFrameCount();
do {
for (int i = 0; i < n; i++) {
if (!animating) {
break;
}
//milliseconds spent on frame decode
long frameDecodeTime = 0;
try {
long before = System.nanoTime();
tmpBitmap = gifDecoder.getNextFrame();
frameDecodeTime = (System.nanoTime() - before) / 1000000;
if (frameCallback != null) {
tmpBitmap = frameCallback.onFrameAvailable(tmpBitmap);
}
if (!animating) {
break;
}
handler.post(updateResults);
} catch (final ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
Log.w(TAG, e);
}
if (!animating) {
break;
}
gifDecoder.advance();
try {
int delay = gifDecoder.getNextDelay();
// Sleep for frame duration minus time already spent on frame decode
// Actually we need next frame decode duration here,
// but I use previous frame time to make code more readable
delay -= frameDecodeTime;
if (delay > 0) {
Thread.sleep(framesDisplayDuration > 0 ? framesDisplayDuration : delay);
}
} catch (final Exception e) {
// suppress any exception
// it can be InterruptedException or IllegalArgumentException
}
}
} while (animating);
if (animationStopCallback != null) {
animationStopCallback.onAnimationStop();
}
}
public OnFrameAvailable getOnFrameAvailable() {
return frameCallback;
}
public void setOnFrameAvailable(OnFrameAvailable frameProcessor) {
this.frameCallback = frameProcessor;
}
public interface OnFrameAvailable {
Bitmap onFrameAvailable(Bitmap bitmap);
}
public OnAnimationStop getOnAnimationStop() {
return animationStopCallback;
}
public void setOnAnimationStop(OnAnimationStop animationStop) {
this.animationStopCallback = animationStop;
}
public interface OnAnimationStop {
void onAnimationStop();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
clear();
}
}
I had to play and pause the Gif image Glide - Cannot stop gif onClick- Getting TransitionDrawable instead of Animate/GifDrawable
The idea is to get drawable from view,checking if it is an instance of Gifdrawable and playing and pausing it.(Hoping the gif image is already playing)
Add this In OnClick of GifImageView
Drawable drawable = ((ImageView) v).getDrawable();
if (drawable instanceof GifDrawable) {
GifDrawable animatable = (GifDrawable) drawable;
if (animatable.isRunning()) {
animatable.stop();
} else {
animatable.start();
}
}
I found the solution of above problem using GifMovieView!!!
GifMovieViewer.java
public class GifMovieViewer extends Activity {
private Button btnStart;
private GifMovieView gif1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gif_movie_viewer);
gif1 = (GifMovieView) findViewById(R.id.gif1);
btnStart = (Button) findViewById(R.id.btnStart);
btnStart.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
gif1.setMovieResource(R.drawable.earth_tilt_animation);
//for pause
// gif1.setPaused(gif1.isPaused());
}
});
}
public void onGifClick(View v) {
GifMovieView gif = (GifMovieView) v;
gif.setPaused(!gif.isPaused());
}
}
So I'm working on a project which needs to cut up a video into multiple frames, and save them as Bitmaps on the device.
I'm using FFmpegMediaMetadataRetriever.getFrameAtTime() to obtain the individual frames, which is working, but is slow. To speed it up a bit I'm trying to implement multiple worker threads which go off and grab the frames, finally responding back to UI via an anonymous function.
I have a class MyVideoProcessor which handles the video processing, and this is called from my EditVideoActivity.
The threads start, and start processing, but shortly afterwards the EditVideoActivity dies (ANR).
From what I can see, there is nothing running on UI (apart from at the very end (which I confirm only runs once)) so not sure why the UI thread is being held up by the worker threads.
EDIT:
So I've switched out FFmpegMediaMetadataRetriever for the standard MediaMetadataRetriever and everything works. BUT I need to use FFmpegMediaMetadataRetriever, as the OPTION_CLOSEST in MMR doesn't work as it should.
EditVideoActivity:
if (mBackgroundThread==null || !mBackgroundThread.isAlive()) {
mBackgroundThread = new Thread(mMyVideoProcessor);
mBackgroundThread.start();
}
MyVideoProcessor:
public class MyVideoProcessor implements Runnable {
private static final String TAG = MyVideoProcessor.class.getSimpleName();
private MyVideo mMyVideo;
private final Context mContext;
public static final int FRAME_CUT_DURATION = 200;
private int mStartFrom = 0;
private int mCurrentDuration = 0;
private int mVideoDuration = 0;
private ArrayList<OnFrameUpdateListener> listeners = new ArrayList<>();
private ExecutorService mProcessors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public MyVideoProcessor(Context context, MyVideo myVideo) {
mContext = context;
mMyVideo = myVideo;
}
public void setOnFrameUpdateListener(OnFrameUpdateListener listener) {
listeners.add(listener);
}
public int getCurrentDuration() {
return mCurrentDuration;
}
public void setStartFrom(int startFrom) {
mStartFrom = startFrom;
}
#Override
public void run() {
if (!mMyVideo.getProcessed()) {
FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
retriever.setDataSource(mContext.getExternalFilesDir(null) + File.separator + mMyVideo.getVideo());
String time = retriever.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION);
retriever.release();
mVideoDuration = Integer.parseInt(time);
int i = 0;
if (mStartFrom > 0) {
Log.d(TAG,"Attempting restore");
i = mStartFrom+1;
}
for ( i=i;i<mVideoDuration;i+=FRAME_CUT_DURATION) {
mProcessors.execute(new ExtractImageExecutor(i));
}
}
}
public class ExtractImageExecutor implements Runnable {
private int mTime;
public ExtractImageExecutor(int time) {
mTime = time;
}
#Override
public void run() {
FFmpegMediaMetadataRetriever retriever = new FFmpegMediaMetadataRetriever();
retriever.setDataSource(mContext.getExternalFilesDir(null) + File.separator + mMyVideo.getVideo());
mCurrentDuration = mTime;
long startTime = System.currentTimeMillis();
Bitmap bitmap = retriever.getFrameAtTime(mTime*1000, FFmpegMediaMetadataRetriever.OPTION_CLOSEST);
long endTime = System.currentTimeMillis();
Log.d(TAG, "Took: " + ((endTime - startTime) / 1000f));
if (bitmap != null) {
try {
int thisFrame = 0;
if (mTime>0) {
thisFrame = mTime/FRAME_CUT_DURATION;
}
//noinspection StringBufferReplaceableByString
StringBuilder frameFilename = new StringBuilder();
frameFilename.append("VIDEO_");
frameFilename.append(thisFrame).append("_");
frameFilename.append(new SimpleDateFormat("yyyyMMddHHmm", Locale.UK).format(new Date()));
frameFilename.append(".jpg");
File frameFile = new File(mContext.getExternalFilesDir(null), frameFilename.toString());
FileOutputStream fos = new FileOutputStream(frameFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.close();
mMyVideo.addFrame(thisFrame, frameFile);
/*for (OnFrameUpdateListener listener : listeners) {
listener.onFrameUpdate(mMyVideo);
}*/
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found: " + e.getMessage());
} catch (IOException e) {
Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
retriever.release();
if ((mTime+FRAME_CUT_DURATION) > mVideoDuration) {
mMyVideo.setProcessed(true);
for (OnFrameUpdateListener listener : listeners) {
listener.onFrameUpdate(mMyVideo);
}
}
}
}
}
EditVideoActivity:
public class EditVideoActivity extends Activity {
private static final String TAG = EditVideoActivity.class.getSimpleName();
private ImageView mImageView;
private MyVideo mMyVideo;
private MyVideoProcessor mMyVideoProcessor;
private Thread mBackgroundThread;
private int mCurrentDuration = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_video);
String videoFilename = getIntent().getStringExtra("videoFilename");
if (videoFilename != null) {
mMyVideo = new MyVideo(MyVideo.TYPE_EXTERIOR,"TEST",new File(videoFilename));
mMyVideoProcessor = new MyVideoProcessor(this,mMyVideo);
} else {
Log.d(TAG, "There was a problem with the video file");
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
Log.d(TAG,"Saving Instance State");
outState.putParcelable("video", mMyVideo);
outState.putInt("currentDuration", mMyVideoProcessor.getCurrentDuration());
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.d(TAG,"Restoring Instance State");
super.onRestoreInstanceState(savedInstanceState);
mMyVideo = (MyVideo) savedInstanceState.getParcelable("video");
mCurrentDuration = savedInstanceState.getInt("currentDuration");
}
#Override
protected void onResume() {
super.onResume();
mMyVideoProcessor = new MyVideoProcessor(this,mMyVideo);
final TextView totalFrames = (TextView) findViewById(R.id.totalFrames);
mImageView = (ImageView) findViewById(R.id.imageView2);
final SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
final ProgressBar progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.animate();
seekBar.setEnabled(false);
OnFrameUpdateListener onFrameUpdateListener = new OnFrameUpdateListener() {
#Override
public void onFrameUpdate(final MyVideo myVideo) {
if (myVideo.getProcessed()) {
File lastFrame = myVideo.getLastFrame();
totalFrames.setText(myVideo.getTotalFrames()+"");
mImageView.setImageBitmap(BitmapFactory.decodeFile(lastFrame.getAbsolutePath()));
seekBar.setEnabled(true);
progressBar.setVisibility(View.GONE);
}
}
};
mMyVideoProcessor.setOnFrameUpdateListener(onFrameUpdateListener);
if (mBackgroundThread==null || !mBackgroundThread.isAlive()) {
mBackgroundThread = new Thread(mMyVideoProcessor);
mBackgroundThread.start();
}
}
}
I am trying to scroll to a substring in a string of 1000 lines. I notice a lag in the scroll and UI thread. So I thought to use a AsyncTask but as its executed I get the text but not scroll. Here is my code
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
SharedPreferences score = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
int chapter_number_bookmark = score.getInt("chapter_number", 89);
int verse_number_bookmark = score.getInt("verse_number", 1);
GoToFunction(chapter_number_bookmark,verse_number_bookmark);
return "Executed";
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(getApplicationContext(), "Executed", Toast.LENGTH_SHORT).show();
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {}
}
The GoToFunction
public void GoToFunction(int chapter, int verse)
{
int scroll_amt;
final TextView shw = (TextView) findViewById(R.id.textViewTab);
SharedPreferences score = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
int chapter_number = chapter;
int verse_number = verse;
String verse_number_str = Integer.toString(verse_number);
SQLiteDatabase as = openOrCreateDatabase("/"+Environment.getExternalStorageDirectory().getPath()+"/tamil/verse", MODE_PRIVATE, null);
Cursor a = as.rawQuery("select * from verse"+chapter_number, null);
a.moveToFirst();
strTitle = a.getString(a.getColumnIndex("title"));
final String strContent_book = a.getString(a.getColumnIndex("content"));
int number = a.getInt(a.getColumnIndex("dialogues"));
tab.setText("\n\n "+strAthi+strTitle+"\n\n "+strVasa+number+"\n\n "+strContent_book);
final int offset_dot = strContent_book.indexOf(verse_number_str_dot);
final int offset_comma = strContent_book.indexOf(verse_number_str_comma);
a.close();
as.close();
if(offset_comma!=-1||offset_dot!=-1)
{
if(offset_comma==-1||(offset_dot<offset_comma))
{
try
{
Toast.makeText(getApplicationContext(), "Athiyayam : "+ chapter_number +" Verse : "+ verse_number, Toast.LENGTH_LONG).show();
**scroll(offset_dot); //Scroll function**
}
catch(Exception e)
{
Log.e("Scroll_comma", "Exception", e);
}
}
else
{
try
{
Toast.makeText(getApplicationContext(), "Athiyayam : "+ chapter_number +" Verse :"+ verse_number, Toast.LENGTH_LONG).show();
scroll(offset_comma);
}
catch(Exception e)
{
Log.e("Scroll", "Exception", e);
}
}
}
else
{
Toast.makeText(getApplicationContext(), "Not found", Toast.LENGTH_LONG).show();
}
}
Scroll Function
public void scroll(final int a)
{
final TextView shw = (TextView) findViewById(R.id.textViewTab);
try
{
mScroll.post(new Runnable() {
#Override
public void run() {
int y = shw.getLayout().getLineForOffset(a); // e.g. I want to scroll to line 40
int n = shw.getLayout().getLineTop(y);
mScroll.scrollTo(0, n);
}
});
}
catch(Exception e)
{
Log.e("scroll", "error", e);
}
}
Here I get the text. But the scroll function doesn't get executed. Also I want to do a spinner activity as the bookmark activity starts and finish as it ends which can be done in PreExecute and PostExecute
paste this code to your layout:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<!-- "your layout" -->
</ScrollView>
From your code you try to update main thread from doInBackground.
#Override
protected String doInBackground(String... params) {
....
GoToFunction(chapter_number_bookmark,verse_number_bookmark);
.....
}
You can't do that. I'm sure you get Exception in logcat. If you want to update anyways, do that through Handler
Lets say we have
private TextView m_txtLog;
private ScrollView m_sv;
So to scroll to last line should be something like:
m_sv.post(new Runnable() {
public void run() {
m_sv.scrollTo(0, m_txtLog.getHeight());
}
});
If you run it from Service or AsyncTask use Handler like:
static final int LOG_MSG = 1;
private Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
String txt;
switch (msg.what) {
case LOG_MSG:
txt = msg.obj.toString();
m_txtLog.append(txt);
m_sv.post(new Runnable() {
public void run() {
m_sv.scrollTo(0, m_txtLog.getHeight());
}
});
break;
default:
super.handleMessage(msg);
}
}
};
And now we can write from AsyncTask:
mHandler.sendMessage(mHandler.obtainMessage(LOG_MSG, "Agent started" + "\n\n"));
As you can see scrollTo wrapped by handler. By this way it will work.
(Tested)
Using asynctask for scrolling is really not necessary in this case. you can simply run the code in the main thread. It will work fine.
because if the user clicks the scrollview while scrolling by asyntask. there will be unexpected behaviour.
And also specify
1. android:hardwareAccelerated="true" in your activity manifest of scrollview
2. In addition to above step - scrollView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
This will use hardware acceleration to your scrollview for smooth scrolling.
i'm a new android programmer, here's my question that i can't solve it.
i have 3 classes, MainActivity, Database and ProgressShow.
in class Database, there's a function to copy a big database in assets. and in ProgressShow, it's used to build a ProgressDialog.
but when i start the program, the dialog didn't show, but it did stoped at the break in handler after about several seconds. it seems that the message queue was stucked when copying the big file. but i don't know why. and here's my program, please help me. thanks.
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Database database = new Database(this);
database.CopyBigDatabase(CommonPara.DB_CONTENT_NAME,
CommonPara.DB_CONTENT_PATH + CommonPara.DB_CONTENT_NAME,
CommonPara.DB_CONTENT_COUNT);
}
}
public class Database
{
private Context mContext;
public Database(Context context)
{
mContext = context;
}
public SQLiteDatabase DbConnection(String file)
{
SQLiteDatabase db = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
{
db = SQLiteDatabase.openOrCreateDatabase(file, null);
}
else
{
}
return db;
}
public void CopyBigDatabase(final String name, final String dest, final int count)
{
new Thread()
{
public void run()
{
final ProgressShow dialog = new ProgressShow(
mContext, "please wait", "wait", ProgressShow.DIALOG_TYPE_BAR,
ProgressShow.DIALOG_DEFAULT_MAX);
dialog.ShowDialog();
try
{
InputStream is;
OutputStream os = new FileOutputStream(dest);
for (int i = 1; i <= count; i++)
{
is = mContext.getAssets().open(name + "." +
String.format("%03d", i));
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0)
{
os.write(buffer, 0, length);
}
os.flush();
is.close();
if(dialog.GetProgress()
< ProgressShow.DIALOG_DEFAULT_MAX -1)
{
dialog.CloseDialog();
}
}
os.close();
}
catch (Exception e)
{
}
finally
{
dialog.CloseDialog();
}
}
}.run();
}
}
public class ProgressShow
{
private ProgressDialog dialog = null;
public static final int DIALOG_TYPE_SPINNER = 0;
public static final int DIALOG_TYPE_BAR = 1;
public static final int DIALOG_DEFAULT_MAX = 100;
public static final int DIALOG_DEFAULT_INCREASE = 1;
#SuppressLint("HandlerLeak")
Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if(msg.what == 0)
{
dialog.show();
}
else
{
dialog.incrementProgressBy(msg.what);
if(GetProgress() >= GetMax())
{
dialog.cancel();
}
}
super.handleMessage(msg);
}
};
public ProgressShow(Context context, String title,
String message, int type, int max)
{
dialog = new ProgressDialog(context);
switch (type)
{
case 0:
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
break;
case 1:
default:
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
break;
}
dialog.setMax(max);
dialog.setTitle(title);
dialog.setMessage(message);
dialog.setIndeterminate(false);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
dialog.setProgress(-dialog.getProgress());
}
public void ShowDialog()
{
handler.sendEmptyMessage(0);
}
public void AddProgress(int increase)
{
handler.sendEmptyMessage(increase);
int a = GetProgress();
}
public int GetProgress()
{
return dialog.getProgress();
}
public void CloseDialog()
{
handler.sendEmptyMessage(GetMax());
}
public int GetMax()
{
return dialog.getMax();
}
}
The ProgressDialog must be showed in the main thread; you are trying to display it from a separate thread), because the handler is running in the thread where it is created.
Your flow is this: you create a new thread, then create a new ProgressShow in this thread. When creating a new ProgressShow object, you create a new Handler object. This handler is created in the new thread. A progress dialog cannot be displayed from a thread other than main.