xamarin 如何使我的小部件彼此独立?

gorkyyrv  于 2023-06-20  发布在  其他
关注(0)|答案(1)|浏览(95)

我已经使用Xamarin为Android创建了一个命令提示符小部件,并且我正在试图弄清楚如何设置我的ListViewFactory类和RemoteViewsService类,以便每个小部件都有单独的数据。
根据我的理解,RemoteViewsService类返回要显示给用户的ListViewFactory对象。问题是我的列表有用户输入,我不知道如何获取对从RemoteViewsService返回的ListViewFactory对象的引用。
WidgetProvider:

namespace CommAndroid
{

[BroadcastReceiver(Label = "CommAndroid", Exported = false, Enabled = true, Name = "com.company.CommAndroid.WidgetProvider",Icon ="@mipmap/terminalappicon")]
[IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE"  })]
[MetaData("android.appwidget.provider", Resource = "@xml/my_widget")]

//Class to inflate and show the widget
public class WidgetProvider : AppWidgetProvider
{
    //Initialize Global Vars for Class
    public static ListViewFactory listViewFactory;
    private Handler handler;

    //Update method for a widget
    public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
    {
        base.OnUpdate(context, appWidgetManager, appWidgetIds);

        foreach (int appwidgetId in appWidgetIds)
        {
            // Build the remote views for the widget
            var widgetView = BuildRemoteViews(context);



            // Update all instances of the widget with the new remote views
            appWidgetManager.NotifyAppWidgetViewDataChanged(appwidgetId, Resource.Id.listView1);
            appWidgetManager.UpdateAppWidget(appwidgetId, widgetView);
   


        }




    }

    //Build the widget view, returns to the update method
    public RemoteViews BuildRemoteViews(Context context)
    {
        //Initialize our widget view, basically pointing to our main widget layout xml
        var widgetView = new RemoteViews(context.PackageName, Resource.Layout.widget_layout);           
       
        //Initializing an intent of our WidgetRemoteViewService, to handle the creation of our list view factory
        //Instructing Widget to use our WidgetRemoteViewService with our ListView
        Intent listViewIntent = new Intent(context, typeof(WidgetRemoteViewService));
        widgetView.SetRemoteAdapter(Resource.Id.listView1, listViewIntent);

        //Initializing an intent for the main widget button click
        var clickIntent = new Intent(context, typeof(WidgetProvider));
        clickIntent.SetAction("com.company.CommAndroid.WIDGET_BUTTON_CLICK");

        // Initializing a new Intent for the delete button click action
        var deleteIntent = new Intent(context, typeof(WidgetProvider));
        deleteIntent.SetAction("com.company.CommAndroid.DELETE_BUTTON_CLICK");

        // Initializing pending intents for the button click actions
        var pendingIntent = PendingIntent.GetBroadcast(context, 5555533, clickIntent, PendingIntentFlags.Mutable);
        var pendingDeleteIntent = PendingIntent.GetBroadcast(context, 55522111, deleteIntent, PendingIntentFlags.Mutable);


        // Associating the pending intents with the respective buttons in the widget layout
        widgetView.SetOnClickPendingIntent(Resource.Id.delete_button, pendingDeleteIntent);
        widgetView.SetOnClickPendingIntent(Resource.Id.widget_button, pendingIntent);

        Intent itemClickIntent = new Intent(context, typeof(WidgetProvider));
        itemClickIntent.SetAction("com.company.CommAndroid.LIST_ITEM_CLICK");
        PendingIntent itemClickPendingIntent = PendingIntent.GetBroadcast(context, 3434514, itemClickIntent, PendingIntentFlags.Mutable);
        widgetView.SetPendingIntentTemplate(Resource.Id.listView1, itemClickPendingIntent);



        // ...

        return widgetView;
    }

    //On Deleted
    public override void OnDeleted(Context context, int[] appWidgetIds)
    {
        base.OnDeleted(context, appWidgetIds);

    }

    //Method to update the listview in widget
    private async void updateListView(Context context, RemoteViews views, bool isDelete, string command)
    {
        //Create a new list factory and view for our widget
        //Create an AppWidgetManager, reference component name, and get appWidgetIds
        listViewFactory = new ListViewFactory(context);

        var widgetView = views;

        AppWidgetManager appWidgetManager = AppWidgetManager.GetInstance(context);
        ComponentName componentName = new ComponentName(context, Java.Lang.Class.FromType(typeof(WidgetProvider)).Name);
        int[] appWidgetIds = appWidgetManager.GetAppWidgetIds(componentName);

        //Checks bool parameter to see if user wants to clear list
        if (isDelete != true)
        {
            //Initialize string result that is returned from querying the command through class TerminalCommands
            //Add the command itself, and the results to the list
            string result = await TerminalCommands.queryCommand(command, context, appWidgetManager, appWidgetIds, widgetView);
            listViewFactory.addCommand("CMD: " + command, result);

            //Create Handler to facilitate Self Scrolling of Listview
            //Set Scroll position, partially update app widget
            //Don't know why this works instead of just setting scroll position, but hey it's android development
            handler = new Handler(Looper.MainLooper);
            handler.PostDelayed(async() =>
            {
                   
                widgetView.SetScrollPosition(Resource.Id.listView1, ListViewFactory.items.Count - 1);
                appWidgetManager.PartiallyUpdateAppWidget(appWidgetIds, widgetView);

            }, 500);
      

        }
        //Bool parameter for user clearing list
        else
        {
            listViewFactory.clearList();
            widgetView.SetEmptyView(Resource.Id.listView1, Resource.Id.empty_view);

        }

        //Update the widget view
        appWidgetManager.NotifyAppWidgetViewDataChanged(appWidgetIds, Resource.Id.listView1);
        appWidgetManager.UpdateAppWidget(componentName, views);

    }


    //Method for receiving broadcasts from broadcast receiver
    public override void OnReceive(Context context, Intent intent)
    {
        //Initialize view for our widget layout
        base.OnReceive(context, intent);

        //Rebuild the view, re-setting the intents ---> Goal is to fix bug where widget stops working after some time
        //Either this or settings flags to mutable
        var widgetView = BuildRemoteViews(context);

        //Command Click
        if (intent.Action == "com.company.CommAndroid.WIDGET_BUTTON_CLICK")
        {
            //Create a new intent of activity InputProvider
            //Start the input provider activity
            Intent inputIntent = new Intent(Application.Context, typeof(InputProvider));
            inputIntent.AddFlags(ActivityFlags.NewTask);
            context.StartActivity(inputIntent);

        }
        //Clear Click
        else if (intent.Action == "com.company.CommAndroid.DELETE_BUTTON_CLICK")
        {
            updateListView(context, widgetView, true, "");
        }
        //User input
        else if (intent.Action == "com.company.CommAndroid.USER_INPUT_SUBMITTED")
        {
            string command = intent.GetStringExtra("user_input");
            updateListView(context, widgetView, false, command);
        }
        else if(intent.Action == "com.company.CommAndroid.LIST_ITEM_CLICK")
        {
            Log.Debug("hi", "we got here");
            string commandText = intent.GetStringExtra("command_text");



        }


    }

}

}
ListViewFactory:

namespace CommAndroid
 {

public class ListViewFactory : Java.Lang.Object, RemoteViewsService.IRemoteViewsFactory
{
    //Globals
    private Context context;
    public static List<string> items;
    public static List<string> results;

    //Constructor to create list view factory
    public ListViewFactory(Context context)
    {
        this.context = context;
        if (items == null)
            items = new List<string>();

        if (results == null)
            results = new List<string>();

        if(items.Count == 0 && results.Count == 0)
        {
            items.Add("CMD: ");
            results.Add("");
        }

        
    }

    public void OnCreate()
    {
        // Initialize your data source
    
    }

    public void removeLast()
    {
        items.RemoveAt(items.Count - 1);
        results.RemoveAt(results.Count - 1);
    }

    //Add's a command to the listview, removes the prior placeholder CMD text
    public void addCommand(string command,string result)
    {
        if (items == null || items.Count < 0)
        {
            items = new List<string>();
        }
        removeLast();
        items.Add(command);
        results.Add(result);
        items.Add("CMD: ");
        results.Add("");
    }

    //Clear the lists
    public void clearList()
    {
        items.Clear();
        results.Clear();
        items.Add("CMD: ");
        results.Add("");
    }

    public void OnDestroy()
    {
        // Cleanup resources if needed
    }

    public int Count => items.Count;

    //Method to create and set the listview view
    public RemoteViews GetViewAt(int position)
    {
        // Create a RemoteViews object for each item in the data source, Referencing our Layout for our List View
        RemoteViews remoteViews = new RemoteViews(context.PackageName, Resource.Layout.listview_layout);

        //Check's if there is items, iterates until there is none
        if (items != null && items.Count > position)
        {
            //Sets text in listview to command items and result items
            //Also checks the commands text to determine what color to output
            remoteViews.SetTextViewText(Resource.Id.command_text, items[position]);
            if (results[position].ToLower().Split(' ')[0] == "invalid")
                remoteViews.SetTextColor(Resource.Id.results_text, Color.Red);
            else
                remoteViews.SetTextColor(Resource.Id.results_text, Color.ParseColor("#52db02"));
            if (items[position].ToLower().Split(' ')[1] == "help" || items[position].ToLower().Split(' ')[1] == "dir" || results[position].ToLower().Split(' ')[0] == "flipping")
            {
              if(results[position].ToLower().Split(' ')[0] != "invalid")
                    remoteViews.SetTextColor(Resource.Id.results_text, Color.White);
            }
               
            remoteViews.SetTextViewText(Resource.Id.results_text, results[position]); 
        }
        
        //If statement checking whether the position in the list is the last one
        //Sets blinking animation to visible
        if (position == items.Count - 1)
        {
            remoteViews.SetViewVisibility(Resource.Id.blinking_dot, ViewStates.Visible);
        }
        else
        {
            remoteViews.SetViewVisibility(Resource.Id.blinking_dot, ViewStates.Gone);
        }

        Intent fillInIntent = new Intent();
        fillInIntent.PutExtra("command_text", items[position]);
       remoteViews.SetOnClickFillInIntent(Resource.Id.listviewlayout, fillInIntent);



        return remoteViews;
    } 

    public RemoteViews LoadingView => null;

    public int ViewTypeCount => 1;

    public long GetItemId(int position)
    {
        return position;
    }

    public bool HasStableIds => true;

    public void OnDataSetChanged()
    {
        // Update your data source if needed
    }
}

}
RemoteViewsService.cs:

namespace CommAndroid
 {

//Handles the creation of the List View Factory Class --> Function to return our ListViewFactory
//Declare as service in android manifest, name is pointer for location

[Service(Enabled = true,  Exported = false, Permission = "android.permission.BIND_REMOTEVIEWS")]
public class WidgetRemoteViewService : RemoteViewsService
{
    public override IRemoteViewsFactory OnGetViewFactory(Intent intent)
    {
        return new ListViewFactory(this);
    }
}

}
我的代码被上传到github repo。https://github.com/chrisz99/CommAndroid

xdyibdwo

xdyibdwo1#

对不起,我从来没有做过小部件。所以这还不是一个完整的答案。
一些问题:

  • 什么样的用户操作会导致创建小部件?
  • 当用户执行该操作时,您的哪些代码会运行?

1.将用户输入存储在类中:

public class UserInputData
{
    public List<string> items;
    public List<string> results;
}

1.更改ListViewFactory构造函数以将其作为参数:

private Context context;
private List<string> items;
private List<string> results;

public ListViewFactory(Context context, UserInputData data)
{
    this.context = context;
    this.items = data.items;
    this.results = data.results;
}

1.更改WidgetRemoteViewService.OnGetViewFactory以传入该数据:

public class WidgetRemoteViewService : RemoteViewsService
{
    // TODO: "Somehow" set this before each OnGetViewFactory call.
    private UserInputData NextData;

    public void SetNextData(UserInputData data)
    {
        NextData = data;
    }

    public override IRemoteViewsFactory OnGetViewFactory(Intent intent)
    {
        return new ListViewFactory(this, NextData);
    }
}

1.* * TODO**在创建每个widget之前设置NextData

  • 缺少的部分是如何/何时设置NextData

我的问题(在这个答案的开头)希望能揭示在代码中需要做的地方。

  • 如果该代码可以直接访问WidgetRemoteViewService的示例,那么它可以在正确的时刻设置NextData
  • 如果该代码没有访问权限,则搜索xamarin MessagingCenter,并阅读有关发布/订阅的信息。试着安排一下。

相关问题