onLoadFinished() not getting called, but onCreateLoader() is called - fragmentpageradapter

I am using CursorLoader to load data from database inside my Fragment, which has a RecyclerView. This fragment is being used inside a ViewPager. The ViewPager is contained inside a ContainerFragment, which in turn is inside an Activity.
The ContainerFragment initializes first 4 loaders out of the 10 required in onActivityCreated(). From the log I can see that the loader(s) are in fact getting created. However, onLoadFinished is not getting called for any of the loaders.
Now the twist comes when I swipe through the ViewPager to the third fragment. Now I see the third loader's onLoadFinished getting called. Now this is probably not called from the fragment but from the PagerAdapter's getItem() method, which in addition to instantiating the fragments, also initializes loaders with their IDs in case they haven't been already.
From all other questions on StackOverflow about onLoadFinished not getting called, I have tried the following solutions which are not working for me:
Using forceLoad()
this.getListView().refreshDrawableState(); -> not tried, and don't understand why this should work. Moreover, I am using RecyclerView.
"Importing the correct class" -> I am using AndroidX, and moreover, the loader does load sometimes. If it was the wrong class, it wouldn't have worked any time right?
"Fragment should use SupportLoaderManager" -> tried replacing in the fragment with getActivity().getSupportLoaderManager. For fragment I think it's just getLoaderManager. It was already working sometimes with just getLoaderManager. (No difference observed)
#onActivityCreated
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
startLoaders();
}
startLoaders()
private void startLoaders() {
while(loaderSeqIndex < CATEGORIES.size() && loaderSeqIndex < 4) {
Bundle bundle = new Bundle();
bundle.putString("category", CATEGORIES.get(loaderSeqIndex));
getLoaderManager().initLoader(loaderSeqIndex++, bundle, this);
}
}
LoaderCallbacks
#NonNull
#Override
public Loader<Cursor> onCreateLoader(int id, #Nullable Bundle args) {
Timber.i("Loader created onCreateLoader called");
CursorLoader loader = ArticleLoader.newCategorizedArticlesInstance(getContext(), args.getString("category"));
loader.registerListener(id, new Loader.OnLoadCompleteListener<Cursor>() {
#Override
public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
int position = loader.getId();
cursorHashMap.put(CATEGORIES.get(position), data);
Timber.i("mPager.getCurrentItem(): " + mPager.getCurrentItem() + " position: " + position);
if (position == mPager.getCurrentItem()) {
ArticleListViewModel model = ViewModelProviders.of(ArticleListContainerFragment.this).get(ArticleListViewModel.class);
model.setCursor(data);
}
}
});
return loader;
}
#Override
public void onLoadFinished(#NonNull Loader<Cursor> loader, Cursor data) {
int position = loader.getId();
cursorHashMap.put(CATEGORIES.get(position), data);
Timber.i("mPager.getCurrentItem(): " + mPager.getCurrentItem() + " position: " + position);
if(position == mPager.getCurrentItem()) {
ArticleListViewModel model = ViewModelProviders.of(this).get(ArticleListViewModel.class);
model.setCursor(data);
// mPagerAdapter.notifyDataSetChanged();
}
}
PagerAdapter's #getView
private class MyPagerAdapter extends FragmentStatePagerAdapter {
...
#Override
public Fragment getItem(int position) {
ArticleListFragment fragment = ArticleListFragment.newInstance();
Bundle bundle = new Bundle();
bundle.putString("category", CATEGORIES.get(position));
bundle.putInt("id",position);
fragment.setArguments(bundle);
// fragment.setCursor(cursorHashMap.get(CATEGORIES.get(position)));
return fragment;
}
...
}
I was expecting timber to print from the #onLoadFinished method just to make sure that it's getting called, which isn't happening.
Something weird that is happening is that:
the cursorHashMap that I am using, get's properly populated I open the app a second time (when refresh doesn't happen). And the cursor get's populated without #onLoadFinished being called.

Related

TextArea is not updating while zip extraction

I am working on a JavaFx application, there i have a script that extract a zip followed by some other operation like updating files etc.
I want to have a textArea that displays whats going on in background, like "Zip extracting...", "Updating xyz file" etc.
Till now i have tried following way:
MyTask<String> task;
task = new MyTask<String>() {
#Override
protected String call() throws Exception {
File path = new File(exportTo.getAbsolutePath());
updateMessage("Extracting modular app to target directory...");
patcher.unZip(appPath.getAbsolutePath(), path.getAbsolutePath());
if (path.exists()) {
AppInfo info = getAppInfo();
patcher.patchAndroid(info, resourceZip, new File(path.getAbsolutePath() + "/" + appPath.getName().substring(0, appPath.getName().lastIndexOf("."))), this);
showOkAlert("Build completed!");
} else {
showOkAlert("Modular app folder not found");
}
return "";
}
#Override
protected void updateProgress(double workDone, double max) {
patcher.reportLogs(message);
}
private String message;
#Override
public void updateMessage(final String message) {
Platform.runLater(() -> patcher.reportLogs(message));
this.message = message;
//updateProgress(0, 0);
}
};
task.run();
MyTask class
abstract class MyTask<T> extends Task<T> {
abstract public void updateMessage(String message);
}
I have tried using updateProgress method, Platform.runLater() but nothing is working.
All the message i printed in textArea are printed after all operation is done.
Please help.
As javadoc for Task states you need to manually create a Thread to execute your Task:
Thread th = new Thread(task);
th.start();
Currently your task is being run on Application UI thread and blocks UI updates.

Android: Updating the UI from an adapter inside a While Loop

I'm using a GridView to display a list of words in a 4-column table. The getView method of my custom adapter checks the width of the word and shrinks it if it doesn't fit. It does this using a recursive check that keeps scaling the text down until it fits.
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + wv.getTextSize()/density + " to " + (wv.getTextSize()/density-1.0f));
wv.setTextSize(wv.getTextSize() / density - 1.0f);
}
}
}).start();
}
Because I'm using a while loop, I am using a new thread to protect against ANR in the unlikely event of an infinite loop. Here's the weird thing: sometimes it works great. And then sometimes I get the following error:
09-26 14:25:31.389 6427-7765/com.myapp.debug E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-7789
Process: com.myapp.debug, PID: 6427
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I tried putting the setTextSize inside a runOnUiThread statement, but I can't get it to work inside the adapter. Ultimately I just want this to work. I think my options are:
Keep the while loop in the UI thread and somehow safeguard it (how?)
Move the setTextSize call to the UI thread (how?)
Something else?
Thanks for your help!
UPDATE: based on Rustam's answer, I used wv.post to write to the UI. However the need to use the while loop meant that setTextSize had to be done in the same thread as the while condition itself. I switched from evaluating the TextView.getTextSize to the Paint.getTextSize, since I could set the Paint's text size without impacting the UI, and therefore inside the offshoot thread. Jerry-rigged, but it seems to work.
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
final Paint mPaint = new Paint(wv.getPaint());
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && mPaint.measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + mPaint.getTextSize()/density + " to " + (mPaint.getTextSize()/density-1.0f));
mPaint.setTextSize(mPaint.getTextSize() - 1.0f);
}
if (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
wv.post(new Runnable() {
#Override
public void run() {
Logg.d(word + " final size=" + mPaint.getTextSize() / density);
wv.setTextSize(mPaint.getTextSize() / density);
}
});
}
}
}).start();
}
try to update like this:
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + wv.getTextSize()/density + " to " + (wv.getTextSize()/density-1.0f));
wv.post(new Runnable() {
public void run() {
wv.setTextSize(wv.getTextSize() / density - 1.0f);
}
});
}
}).start();
}

Debugging Package Manager Console Update-Database Seed Method

I wanted to debug the Seed() method in my Entity Framework database configuration class when I run Update-Database from the Package Manager Console but didn't know how to do it. I wanted to share the solution with others in case they have the same issue.
Here is similar question with a solution that works really well.
It does NOT require Thread.Sleep.
Just Launches the debugger using this code.
Clipped from the answer
if (!System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Launch();
The way I solved this was to open a new instance of Visual Studio and then open the same solution in this new instance of Visual Studio. I then attached the debugger in this new instance to the old instance (devenv.exe) while running the update-database command. This allowed me to debug the Seed method.
Just to make sure I didn't miss the breakpoint by not attaching in time I added a Thread.Sleep before the breakpoint.
I hope this helps someone.
If you need to get a specific variable's value, a quick hack is to throw an exception:
throw new Exception(variable);
A cleaner solution (I guess this requires EF 6) would IMHO be to call update-database from code:
var configuration = new DbMigrationsConfiguration<TContext>();
var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();
This allows you to debug the Seed method.
You may take this one step further and construct a unit test (or, more precisely, an integration test) that creates an empty test database, applies all EF migrations, runs the Seed method, and drops the test database again:
var configuration = new DbMigrationsConfiguration<TContext>();
Database.Delete("TestDatabaseNameOrConnectionString");
var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();
Database.Delete("TestDatabaseNameOrConnectionString");
But be careful not to run this against your development database!
I know this is an old question, but if all you want is messages, and you don't care to include references to WinForms in your project, I made some simple debug window where I can send Trace events.
For more serious and step-by-step debugging, I'll open another Visual Studio instance, but it's not necessary for simple stuff.
This is the whole code:
SeedApplicationContext.cs
using System;
using System.Data.Entity;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace Data.Persistence.Migrations.SeedDebug
{
public class SeedApplicationContext<T> : ApplicationContext
where T : DbContext
{
private class SeedTraceListener : TraceListener
{
private readonly SeedApplicationContext<T> _appContext;
public SeedTraceListener(SeedApplicationContext<T> appContext)
{
_appContext = appContext;
}
public override void Write(string message)
{
_appContext.WriteDebugText(message);
}
public override void WriteLine(string message)
{
_appContext.WriteDebugLine(message);
}
}
private Form _debugForm;
private TextBox _debugTextBox;
private TraceListener _traceListener;
private readonly Action<T> _seedAction;
private readonly T _dbcontext;
public Exception Exception { get; private set; }
public bool WaitBeforeExit { get; private set; }
public SeedApplicationContext(Action<T> seedAction, T dbcontext, bool waitBeforeExit = false)
{
_dbcontext = dbcontext;
_seedAction = seedAction;
WaitBeforeExit = waitBeforeExit;
_traceListener = new SeedTraceListener(this);
CreateDebugForm();
MainForm = _debugForm;
Trace.Listeners.Add(_traceListener);
}
private void CreateDebugForm()
{
var textbox = new TextBox {Multiline = true, Dock = DockStyle.Fill, ScrollBars = ScrollBars.Both, WordWrap = false};
var form = new Form {Font = new Font(#"Lucida Console", 8), Text = "Seed Trace"};
form.Controls.Add(tb);
form.Shown += OnFormShown;
_debugForm = form;
_debugTextBox = textbox;
}
private void OnFormShown(object sender, EventArgs eventArgs)
{
WriteDebugLine("Initializing seed...");
try
{
_seedAction(_dbcontext);
if(!WaitBeforeExit)
_debugForm.Close();
else
WriteDebugLine("Finished seed. Close this window to continue");
}
catch (Exception e)
{
Exception = e;
var einner = e;
while (einner != null)
{
WriteDebugLine(string.Format("[Exception {0}] {1}", einner.GetType(), einner.Message));
WriteDebugLine(einner.StackTrace);
einner = einner.InnerException;
if (einner != null)
WriteDebugLine("------- Inner Exception -------");
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing && _traceListener != null)
{
Trace.Listeners.Remove(_traceListener);
_traceListener.Dispose();
_traceListener = null;
}
base.Dispose(disposing);
}
private void WriteDebugText(string message)
{
_debugTextBox.Text += message;
Application.DoEvents();
}
private void WriteDebugLine(string message)
{
WriteDebugText(message + Environment.NewLine);
}
}
}
And on your standard Configuration.cs
// ...
using System.Windows.Forms;
using Data.Persistence.Migrations.SeedDebug;
// ...
namespace Data.Persistence.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
// Migrations configuration here
}
protected override void Seed(MyContext context)
{
// Create our application context which will host our debug window and message loop
var appContext = new SeedApplicationContext<MyContext>(SeedInternal, context, false);
Application.Run(appContext);
var e = appContext.Exception;
Application.Exit();
// Rethrow the exception to the package manager console
if (e != null)
throw e;
}
// Our original Seed method, now with Trace support!
private void SeedInternal(MyContext context)
{
// ...
Trace.WriteLine("I'm seeding!")
// ...
}
}
}
Uh Debugging is one thing but don't forget to call:
context.Update()
Also don't wrap in try catch without a good inner exceptions spill to the console.
https://coderwall.com/p/fbcyaw/debug-into-entity-framework-code-first
with catch (DbEntityValidationException ex)
I have 2 workarounds (without Debugger.Launch() since it doesn't work for me):
To print message in Package Manager Console use exception:
throw new Exception("Your message");
Another way is to print message in file by creating a cmd process:
// Logs to file {solution folder}\seed.log data from Seed method (for DEBUG only)
private void Log(string msg)
{
string echoCmd = $"/C echo {DateTime.Now} - {msg} >> seed.log";
System.Diagnostics.Process.Start("cmd.exe", echoCmd);
}

How do I set up a listener?

I think I need a listener, but can’t set it up.
In the code below, both the time dialog and date dialog work. The little dialog w/ rollers comes up, I select a date/time, then select and I have my date. But I cannot get the text input to work.
I have tried several iterations of this code from a sample program which had:
bnBodyEntered = true;
bodyDialog = new TextInputDialog(this);
bodyDialog.setText("body");
This code showed the text dialog w/ alphabet roller, but the program ran past it and when I entered a few letters and selected nothing happened. That makes since as I did not set up a listener. So I added (as noted)
bodyDialog.setOnDismissListener(textSetListener);
using several "setOn----Listener" values and It either still runs past, or I get various compile errors.
This is for my WIMMOne watch, so version 7 and no virtual keyboard. It needs to be a dialog.
(Note: I deleted gobs of commented code before posting - possibly something more, so if it doesn't make since - sorry. )
Thanks, Clark
/******************************
* Wimm imports
******************************/
import com.wimm.framework.app.Dialog;
import com.wimm.framework.app.DatePickerDialog;
import com.wimm.framework.app.DatePickerDialog.OnDateSelectedListener;
import com.wimm.framework.app.LauncherActivity;
import com.wimm.framework.app.TextInputDialog;
import com.wimm.framework.app.TimePickerDialog;
import com.wimm.framework.app.TimePickerDialog.OnTimeSelectedListener;
# Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d("RemindEA","01S onCrt");
setContentView(R.layout.reminder_edit_activity);
. . .
. . .
mBodyText = (EditText)findViewById(R.id.body);
mTimeButton = (Button)findViewById(R.id.reminder_time);
mDateButton = (Button)findViewById(R.id.reminder_date);
mConfirmButton = (Button)findViewById(R.id.confirm);
Log.d("RemindEA","10S onCrtV df");
//------------- TEXT INPUT
mBodyText.setOnClickListener( new View.OnClickListener()
{
public void onClick(View v)
{
Log.d("RemindEA","21> onClkV:Body");
showDialog(BODY_DIALOG_ID);
Log.d("RemindEA","22< onClkV:Body:" + strBody);
mBodyText.setText(bodyDialog.getText());
return;
}
} );
//-------------------------- TIME INPUT
mTimeButton.setOnClickListener( new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Log.d("RemindEA","25> onClkV:Time");
showDialog(TIME_DIALOG_ID);
Log.d("RemindEA","26< onClkV:Time");
}
} );
//-------------------------- DATE INPUT
mDateButton.setOnClickListener( new View.OnClickListener()
{
#Override
public void onClick(View v)
{
Log.d("RemindEA","30> onClkV:Time");
showDialog(DATE_DIALOG_ID);
Log.d("RemindEA","31< onClkV:Time");
}
} );
protected Dialog onCreateDialog(int id)
{
Log.d("RemindEA","70S Dialog:" + id);
switch (id)
{
// if we want a time dialog--------------
case TIME_DIALOG_ID:
Log.d("RemindEA","71S TIME_DIALOG_ID");
timeDialog = new TimePickerDialog(this);
timeDialog.setTimeSelectedListener(timeSetListener);
timeDialog.setTime(mCalendar.get(Calendar.HOUR_OF_DAY),
mCalendar.get(Calendar.MINUTE));
Log.d("RemindEA","73S TIME_D:" + timeDialog);
return timeDialog;
// if we want a date dialog ---------------
case DATE_DIALOG_ID:
Log.d("RemindEA","75S DATE_DIALOG_ID-26");
mCalendar = Calendar.getInstance();
Log.d("RemindEA","76S DATE_DIALOG_ID-27");
dateDialog = new DatePickerDialog(this);
dateDialog.setDateSelectedListener(dateSetListener);
dateDialog.setDate(mCalendar.get(Calendar.DAY_OF_MONTH),
mCalendar.get(Calendar.MONTH ));
Log.d("RemindEA","77S DATE_D:" + dateDialog);
return dateDialog;
// if we want text input dialog ---------------------
case BODY_DIALOG_ID:
Log.d("RemindEA","80S BODY_DIALOG_ID");
bnBodyEntered = true;
Log.d("RemindEA","81S TITLE");
bodyDialog = new TextInputDialog(this);
//---> ADDED LINE BELOW TO CREATE A LISTENER: GET ERROR <-----
// ERROR BELOW: setOnDismissListener cannot be resolved to a type
bodyDialog.setOnDismissListener(textSetListener);
//------------------------------------------
//ALSO //.setOnKeyListener(onKeyListener) - others
Log.d("RemindEA","82S New");
bodyDialog.setText("body");
Log.d("RemindEA","83X:" + bodyDialog);
return bodyDialog;
}
return null;
}
private OnTimeSelectedListener timeSetListener = new OnTimeSelectedListener()
{
public void onTimeSelected(int arg0, int arg1)
{
Log.d("RemindEA","90S onTimeSelected");
intHH = arg0;
intMN = arg1;
Log.d("RemindEA","91S schedule:" + intHH + ":" + intMN);
//updateDisplay();
}
};
private OnDateSelectedListener dateSetListener = new OnDateSelectedListener()
{
public void onDateSelected(int arg0, int arg1)
{
Log.d("RemindEA","92S onTimeSelected");
intDD = arg0;
intMM = arg1;
Log.d("RemindEA","93S schedule:" + intDD + ":" + intMM);
//updateDisplay();
}
};
//------------------------------------
// -----> ERROR: setOnDismissListener cannot be resolved to a type
private setOnDismissListener textSetListener = new setOnDismissListener
//------------------------------------
{
private void textSetListener() // bodySetListener ()
{
Log.d("RemindEA","100S BodyLisner"); //<- does not get here on <done>
return;
}
};
After many hours of guess and error, this worked.
I don't think the "Return bodyDialog" does anything; I ended up updating the EditText field directly from the listener. "arg0" has a long name of the dialog - nothing usefull. But you have it have it to prevent compile errors.
This probably is unique to the WIMMOne where there is no virtual keyboard and you need to use their text input dialog.
protected Dialog onCreateDialog(int id)
{
switch (id)
. . . .
case BODY_DIALOG_ID:
bodyDialog = new TextInputDialog(this);
bodyDialog.setOnDismissListener(textSetListener);
return bodyDialog;
private OnDismissListener textSetListener = new OnDismissListener()
{
#Override
public void onDismiss(DialogInterface arg0)
{
Log.d("RemindEA","101S BodyLisner" + arg0);
String strxx = bodyDialog.getText();
mBodyText.setText(strxx);
}
};

SmartGWT - Cannot change configuration property "x" after the component created

From this example http://www.smartclient.com/smartgwt/showcase/#tree_databinding_local , I started to implement my own tree structure dynamically (TreeGrid). When I try to render it, I get this error (title).
public class ProjectTreeGridScreen extends Screen {
Tree tree;
#Override
protected void onLoad() {
super.onLoad();
TreeGrid treeGrid = new TreeGrid();
setPageTitle(Util.C.projectListTitle());
treeGrid.setWidth(600);
treeGrid.setHeight(400);
TreeGridField projectTree = new TreeGridField("ProjectName", "Project Tree");
TreeGridField projectPath = new TreeGridField("ProjectPath", "Complete path");
TreeGridField projectDescription = new TreeGridField("ProjectDescription", "Description");
TreeGridField projectInfo = new TreeGridField("ProjectInfo", "Information");
treeGrid.setFields(projectTree, projectPath, projectDescription, projectInfo);
treeGrid.setData(tree);
add(treeGrid);
}
#Override
protected void onInitUI() {
super.onInitUI();
tree = new Tree();
tree.setModelType(TreeModelType.PARENT);
tree.setNameProperty("ProjectName");
tree.setIdField("ProjectItem");
tree.setParentIdField("ProjectParent");
tree.setShowRoot(true);
populateProjects();
}
protected void populateProjects() {
Util.PROJECT_SVC.visibleProjects(
new ScreenLoadCallback<List<Project>>(this) {
#Override
public void preDisplay(final List<Project> result) {
tree.setData(ProjectTreeGridBuilder.fromRepositories(result));
}
});
}
}
what do you mean by "x"? Normally, If a component has been drawn on the window (implicit or explicit call to draw), you cannot change it's properties values. So the only possible solution is to recreate the object with the new X value each time it is changing.

Resources