Particularly the Navigation between the Views over the ViewModels looks like that:
ShowViewModel<InfoViewModel>();
Or between normal Activities:
context.StartActivity(typeof(InfoActivity));
The problem I face now (actually I solved it in one of my latest projects by using Tinymessenger - its an event aggregator/messenger for loosely coupled communication) but I would like to know if theres an other way!
I'm creating an optionsmenu:
public abstract class BaseActivityWithoutTabs<T> : MvxActivity where T : class, IMvxViewModel
{
public override bool OnCreateOptionsMenu(IMenu menu)
{
return ActivitiesHelper.CreateOptionsMenu(menu);
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
return ActivitiesHelper.CreateOnOptionsItemSelectedEvent(item, this);
}
}
InfoActivity derives from this BaseActivityWithoutTabs.
In the ActivitiesHelper Class (from above code) I'm creating the menu and the events:
public class ActivitiesHelper
{
private const int einstellungenItemId = 0;
private const int infoItemId = 1;
public static bool CreateOptionsMenu(IMenu menu)
{
// GroupId, ItemId, OrderId
menu.Add(0, einstellungenItemId, 0, "Einstellungen").SetIcon(Android.Resource.Drawable.IcMenuManage);
menu.Add(0, infoItemId, 1, "Info").SetIcon(Android.Resource.Drawable.IcMenuInfoDetails);
return true;
}
public static bool CreateOnOptionsItemSelectedEvent(IMenuItem item, Context context)
{
var id = item.ItemId + 1; // (Id is zero-based :)
if (id == 1) // First Item
{
context.StartActivity(typeof(SettingsShowActivity));
}
else if (id == 2) // Second Item
{
context.StartActivity(typeof(InfoActivity)); //doesn't work...
}
return true;
}
}
As you see I do here "StartActivity".. it works for the first "SettingsShowActivity" but thats an PreferenceActivity, so there no reason why it should fail. The problem is, that I would like to Start here the InfoActivity(as you see in code - Second Item) and this doesn't work. It opens the Activity but the List doesn't gets filled.
But if I go to a ViewModel in my project and call: ShowViewModel<InfoViewModel>();it works fine but this is on that place (in the ActivitiesHelper Class) not available/possible!
public class InfoViewModel : MvxViewModel
{
public InfoViewModel()
{
Info info = new Info();
info.Key = "ITS A KEYY";
info.Value = "here we got a value";
ObservableCollection<Info> asd = new ObservableCollection<Info>();
asd.Add(info);
Infos = asd;
}
private ObservableCollection<Info> infos = new ObservableCollection<Info>();
public ObservableCollection<Info> Infos
{
get
{
return infos;
}
set
{
infos = value;
RaisePropertyChanged(() => Infos);
}
}
}
Any suggestions?
I have no idea what you are talking about. Seriously, you've just dumped a lot of stuff on the screen.
I think you've gotten yourself very confused - good luck trying to work out what on earth you've done.
One basic answer is that you can navigate to an Mvx-based Activity anywhere you want to simply by:
creating an MvxViewModelRequest - https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross/ViewModels/MvxViewModelRequest.cs
converting the request to an Intent using the IMvxAndroidViewModelRequestTranslator singleton - https://github.com/slodge/MvvmCross/blob/v3/Cirrious/Cirrious.MvvmCross.Droid/Views/IMvxAndroidViewModelRequestTranslator.cs
starting that Intent
However, I seriously suggest you step out of your current mess and consider a cleaner application flow.
Related
I am developing an Android app that is recognising the activity the user us doing every 3 seconds (has to be that frequent by design) (e.g. static, walking, running). I have an Activity table in my database that increments the following values:
private int activeTime;
private int longestInactivityInterval;
private int currentInactivityInterval;
private int averageInactInterval;
Those are presented in a fragment. Currently, it is very "sensitive". For example, if the user is static (i.e. laying on their bed) and they pull their phone out of the pocket it will recognise activity like "walking". The history of recognised activities would look like that:
static
static
walking
static
static
How can I make sure that this incidental "walking" recognised activity is recognised as "static". Is there a way how I can correct that?
This is the class that is doing the Activity monitoring (incrementing values depending on what activity is recognised.
public class ActivityMonitor implements Observer, IActivityMonitor {
private User mUser;
private IActivityDataManager mDataManager;
public ActivityMonitor(IActivityDataManager dataManager) {
mDataManager = dataManager;
}
#Override
public void update(Observable observable, Object activity) {
monitorActivity(activity);
}
private void monitorActivity(Object activityClass) {
switch ((int) activityClass) {
case 0:
//activity = "walking";
case 1:
//activity = "running";
case 3:
//activity = "cycling";
mDataManager.incActiveTime();
mDataManager.clearCurrentInacInterval();
break;
case 2:
//activity = "static";
mDataManager.incCurrentInacInterval();
break;
}
}
I found a solution to the problem myself. I am using apache's common CircularFifoQueue with set size to 2.
This is how my solution looks like:
private void monitorActivity(Object activityClass) {
int activityInt = (int) activityClass;
correctionList.add(activityInt);
int correctResult = applyCorrection(activityInt);
if (correctResult == correctionList.size()) {
mDataManager.incActiveTime();
mDataManager.clearCurrentInacInterval();
} else {
mDataManager.incCurrentInacInterval();
}
}
private int applyCorrection(int classInt) {
int count = 0;
for (int item : correctionList) {
if (item == 0 || item == 1 || item == 3) {
count++;
}
}
return count;
}
Basically, it adds the classInt which could be (0,1,2 or 3) - walking = 0, running = 1, cycling = 3 and static = 2. The applyCorrection method looks through the queue with size 2 (this plays the role of the factor, 2 works great for me) and counts and checks the integers. If the returned count correctResult is 2 that means that the activity is for sure of time ACTIVE (1,2,3) and not STATIC (2).
In Android, how do I take an action whenever a variable changes?
So I want to implement a listener for an object I created. What I want it to do is execute a block of code when its value changes from false to true.
As I am following this thread, I can't understand where the person wants us to implement the last block of code containing the logic for the listener.
Could someone, hopefully, guide me in the right direction?
(This question is being asked here as I don't have enough rep. points)
That last bit of example code triggers the listener, so it basically needs to be run whenever the "event" occurs. In this case the "event" is whenever (wherever in the code) the value of the variable changes.
If you have a setter and that is the only place the value changes, that is where you'd put it. If you are changing the value in multiple places throughout your code, I would make a new private method (call it signalChanged), put your code there, and then call it immediately after the variable assignment in the cases you want the listener to fire.
Here's an example (some code borrowed from linked answer, haven't checked that it compiles).
public class MyObj
{
public MyObj(int value)
{
setValue(value);
}
private int myValue;
public int getValue() { return myValue; }
public void setValue( int value )
{
if (value != myValue)
{
myValue = value;
signalChanged();
}
}
public interface VariableChangeListener
{
public void onVariableChanged(Object... variableThatHasChanged);
}
private VariableChangeListener variableChangeListener;
public void setVariableChangeListener(VariableChangeListener variableChangeListener)
{
this.variableChangeListener = variableChangeListener;
}
private void signalChanged()
{
if (variableChangeListener != null)
variableChangeListener.onVariableChanged(myValue);
}
}
you have to create a callback interface
here is a good about custom listener tutorial
here is a sample
public class MyObj {
VariableChanger onVariableChanged ;
public void setOnVariableChanged(VariableChanger onVariableChanged) {
this.onVariableChanged = onVariableChanged;
}
void log(){
boolean changed = false;
onVariableChanged.onVariableChanged();
//this will call it
}
interface VariableChanger{
void onVariableChanged();
}
}
class logic {
MyObj mo = new MyObj();
void main(){
mo.setOnVariableChanged(new MyObj.VariableChanger() {
#Override
public void onVariableChanged() {
//do your action
}
});
}
}
In Android, like any language, most developper uses logic comparisons to check values (if, else, switch, =, !=, >, <, etc) or Event (signal)
What kind of listener do you want to implement?
I am using the library for one of my project (https://github.com/googlemaps/android-maps-utils)
This library let me create cluster on the google map, but I was wondering if it is possible to cluster my marker by group. For example, I want to cluster only markers that are "Friends" and cluster the others who are only "Coworker" and etc... (Maybe not the best example, but I hope that you understand)
My idea was to use multiple ClusterManager but I didn't tried it and don't really know if it is the best solution or even a good solution.
I found the solution for my problem. The better solution is to manage multiple Clustermanagers if you want to create multiple group.
By the way, all the credits for the answer goes to #Brody on the Github :
Here the link : https://github.com/googlemaps/android-maps-utils/issues/100#event-153755438
Using multiple ClusterManager is cumbersome. I think it is easier to use multiple Algorithm with a wrapper.
The wrapper should choose the correct algorithm according to the item properties. The only requirement is that all items must have a common parent class (Item in the example below).
public class MultiAlgorithm<T extends ClusterItem> implements Algorithm<T> {
private final Algorithm<T> friendsAlgorithm;
private final Algorithm<T> coworkerAlgorithm;
public MultiAlgorithm() {
friendsAlgorithm = new NonHierarchicalDistanceBasedAlgorithm<>();
coworkerAlgorithm = new NonHierarchicalDistanceBasedAlgorithm<>();
}
private Algorithm<T> getAlgorithm(T item) {
// TODO Return the correct algorithm based on 'item' properties
}
#Override
public void addItem(T item) {
getAlgorithm(item).addItem(item);
}
#Override
public void addItems(Collection<T> collection) {
for (T item : collection) {
getAlgorithm(item).addItem(item);
}
}
#Override
public void clearItems() {
friendsAlgorithm.clearItems();
coworkerAlgorithm.clearItems();
}
#Override
public void removeItem(T item) {
getAlgorithm(item).removeItem(item);
}
#SuppressWarnings("unchecked")
#Override
public Set<? extends Cluster<T>> getClusters(double zoom) {
// Use a non-typed Set to prevent some generic issue on the result.addAll() method
Set result = new HashSet<>(friendsAlgorithm.getClusters(zoom));
result.addAll(coworkerAlgorithm.getClusters(zoom));
return result;
}
#Override
public Collection<T> getItems() {
Collection<T> result = new ArrayList<>(friendsAlgorithm.);
result.addAll(coworkerAlgorithm.getItems());
return result;
}
}
Usage: clusterManager.setAlgorithm(new MultiAlgorithm<Item>());
I'm working in my Android project developing with a HTC Desire (Gingerbread 2.3.7) and a Google Nexus 7 (Jelly Bean 4.3). I need to send some data from MainActivity to InfoActivity, so I use an intent. In this InfoActivity, I also have a menu item in the action bar to refresh the info.
In InfoActivity I show the data to the user. But this is not the problem, the problem is with the menu. Look at the following code:
public class ShowInfoActivity extends ActionBarActivity {
private MenuItem menuItem = null;
// ...
#Override
protected void onCreate(Bundle savedInstanceState) {
// ...
new OneTask().execute(...);
// ...
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
case R.id.refresh:
menuItem = item;
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private class OneTask extends AsyncTask<Object, Void, String> {
// ...
#Override
protected void onPreExecute() {
MenuItemCompat.setActionView(menuItem,
R.layout.actionbar_indeterminate_progress);
MenuItemCompat.expandActionView(menuItem);
}
// ...
#Override
protected void onPostExecute(String result) {
MenuItemCompat.collapseActionView(menuItem);
MenuItemCompat.setActionView(menuItem, null);
}
}
Obviously, the first time it's executed, menuItem=null, so it must crash. Incredibly, in HTC it works fine but in Nexus it obviously crashes. Why is this different between devices?
PS: I already solved it, but I want to know why this behaviour...
When in doubt, always check the source code. If you look at MenuItemCompat.java you'll find that it switches based on the API level like so:
static final MenuVersionImpl IMPL;
static {
final int version = android.os.Build.VERSION.SDK_INT;
if (version >= 14) {
IMPL = new IcsMenuVersionImpl();
} else if (version >= 11) {
IMPL = new HoneycombMenuVersionImpl();
} else {
IMPL = new BaseMenuVersionImpl();
}
}
The base setActionView method for the base implementation (which is used for 2.3 devices) just returns the MenuItem, so it wouldn't ever throw the exception:
#Override
public MenuItem setActionView(MenuItem item, View view) {
return item;
}
The HoneycombMenuVersionImpl, on the other hand, delegates to another class:
#Override
public boolean setShowAsAction(MenuItem item, int actionEnum) {
MenuItemCompatHoneycomb.setShowAsAction(item, actionEnum);
return true;
}
And the delegate class attempts to call the actual method on the MenuItem, which will throw an exception:
public static void setShowAsAction(MenuItem item, int actionEnum) {
item.setShowAsAction(actionEnum);
}
In this particular example, checking the source code answers your question and shows you a solid strategy for dealing with compatibility across different versions of Android.
setActionView()/collapseActionView() are native functions in Android 4 ... passing null MenuItem will surely be frowned upon.
On your Android 2.3 device, the compatibility library (android.support.v4) has it's own internal implementations of setActionView()/collapseActionView() that are likely more resilient to bad input.
I always have this problem of java.lang.IllegalStateException:Could not execute method of the activity. I was planning to perform an android component event (ex. Button event - indicating the number of times this button was clicked). Here's the code snippet for this problem:
interface Selection {
public void clicked();
}
public class ParentClass extends FragmentActivity {
// fTabs : FragmentTabHost
// tabs : Map<String, Selection>
private void initialize() {
// fistFrag : FirstChildClass = new FirstChildClass()
// secondFrag : SecondChildClass = new SecondChildClass()
tabs.put("first", firstFrag);
tabs.put("second", secondFrag);
fTabs.add(fTab.newTabSpec("first").setTitle("First"), firstFrag.getClass(), null)
fTabs.add(fTab.newTabSpec("second").setTitle("Second"), secondFrag.getClass(), null)
}
#Override
public void onBackPressed() {
tabs.get(fTabHost.getCurrentTabTag()).clicked();
}
}
public class FirstChildClass extends Fragment implements Selection {
// data : TextView
// hit : int = 0
#Override
public void clicked() {
data.setText(String.format("Hit Count: %d", ++hit));
}
}
public class SecondChildClass extends Fragment implements Selection {
// data : TextView
// hit : int = 0
#Override
public void clicked() {
data.setText(String.format("Hit Count: %d", ++hit));
}
}
I've tried to assure of clicked() works view interfacing approach by invoking a message on Logcat and it worked but when I used Button the error above always prompts me. I've checked if data is null and it returned true. I am a little bit confused, I've tried to check nullity of data from the Activity methods is returns false but when I access any method override by an interface it always return true. Is there a way to solve this?
Here's a way my friend told me to solve this problem. Using getSupportFragmentManager. He told me also that creating an Activity or Fragment using its constructor isn't applicable on the Android platform. So I switched by to the conventional way of adding tabs to FragmentTabHost.
#Override
public void onBackPressed() {
//tabs.get(fTabHost.getCurrentTabTag()).clicked();
((Selection) getSupportFragmentManager().findByFragmentByTag(fTabHost.getCurrentTabTag()).clicked();
}