LoginSignup
2
1

More than 5 years have passed since last update.

AndroidのLayoutのxmlファイルからViewへのアクセスクラスを生成してみる

Last updated at Posted at 2017-06-20

最低限で

    public class Control
    {
        public string Name { get; set; }
        public string Id { get; set; }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var filepath = args[0];
            var nameSpaceName = args[1];

            var xml = File.ReadAllText(filepath);
            var xdoc = XDocument.Parse(xml);

            var reader = xdoc.CreateReader();
            var controls = new List<Control>();
            while (reader.Read())
            {
                var control = new Control();
                control.Name = reader.Name;
                if (reader.MoveToAttribute("android:id"))
                {
                    control.Id = reader.Value.Split('/')[1];
                    controls.Add(control);
                }
            }

            var sb = new StringBuilder();
            var classname = Path.GetFileNameWithoutExtension(filepath) + "_holder";

            sb.AppendLine(@"using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;");

            sb.AppendLine($"public class {classname} : IDisposable");
            sb.AppendLine("{");

            foreach (var control in controls)
            {
                sb.Append("\t");
                sb.Append($"public {control.Name} {control.Id} ");
                sb.Append("{ get; }");
                sb.AppendLine();
            }
            sb.AppendLine();

            sb.Append("\t");
            sb.Append($"public {classname}(View view)");
            sb.AppendLine();

            sb.AppendLine("\t{");

            foreach (var control in controls)
            {
                sb.Append("\t");
                sb.Append("\t");
                sb.Append($"{control.Id} = view.FindViewById<{control.Name}>({nameSpaceName}.Resource.Id.{control.Id});");
                sb.AppendLine();
            }

            sb.AppendLine("\t}");

            sb.AppendLine($"\tpublic void Dispose()");
            sb.AppendLine("\t{");

            foreach (var control in controls)
            {
                sb.Append("\t");
                sb.Append("\t");
                sb.Append($"{control.Id}.Dispose();");
                sb.AppendLine();
            }
            sb.AppendLine("\t}");

            sb.AppendLine("}");

            Console.Write(sb);

            var file = File.Create($"{classname}.cs");

            TextWriter text = new StreamWriter(file);
            text.Write(sb.ToString());

            text.Flush();
            text.Dispose();

            Console.Read();
        }
    }

こんなxmlがあったら


<?xml version="1.0" encoding="utf-8"?>

<ScrollView
  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">

  <LinearLayout
    android:id="@+id/root_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <RelativeLayout
      android:id="@+id/language_settings_container"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="@drawable/clickable"
      android:clickable="true"
      android:padding="@dimen/space_16dp">

      <TextView
        android:id="@+id/txt_title"
        style="@style/SettingTitleText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toLeftOf="@+id/txt_language"
        android:layout_toStartOf="@+id/txt_language"
        android:ellipsize="end"
        android:lines="1"
        android:text="@string/settings_language" />

      <TextView
        android:id="@+id/setting_description"
        style="@style/SettingDescriptionText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/txt_title"
        android:layout_marginTop="@dimen/space_8dp"
        android:layout_toLeftOf="@+id/txt_language"
        android:layout_toStartOf="@+id/txt_language"
        android:text="@string/settings_language_description" />

      <TextView
        android:id="@+id/txt_language"
        style="@style/SessionTag"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginLeft="@dimen/space_16dp"
        android:layout_marginStart="@dimen/space_16dp"
        android:background="@drawable/tag_language"
        android:padding="@dimen/space_8dp"
        tools:text="@string/lang_en" />

    </RelativeLayout>

    <View
      android:id="@+id/heads_up_border_top"
      style="@style/Border" />

    <DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView
      android:id="@+id/local_time_switch_row"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:settingDescription="@string/settings_local_time_description"
      app:settingTitle="@string/settings_local_time" />

    <View
      android:id="@+id/heads_up_border_bottom"
      style="@style/Border" />

    <RelativeLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:padding="@dimen/space_16dp">

      <TextView
        style="@style/TextSubheading"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/settings_notification_group"
        android:textColor="?attr/colorPrimary" />

    </RelativeLayout>

    <DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView
      android:id="@+id/notification_switch_row"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:settingDescription="@string/settings_notification_description"
      app:settingTitle="@string/settings_notification" />

    <View
      style="@style/Border" />

    <DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView
      android:id="@+id/heads_up_switch_row"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:settingDescription="@string/settings_heads_up_notification_description"
      app:settingTitle="@string/settings_heads_up_notification" />

    <View
      android:id="@+id/heads_up_border"
      style="@style/Border" />

    <RelativeLayout
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:padding="@dimen/space_16dp">

      <TextView
        android:id="@+id/developer_menu_title"
        style="@style/TextSubheading"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/settings_developer_group"
        android:textColor="?attr/colorPrimary" />

      <TextView
        android:id="@+id/developer_menu_tips"
        style="@style/SettingDescriptionText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/developer_menu_title"
        android:layout_marginTop="@dimen/space_8dp"
        android:text="@string/settings_developer_tips" />
    </RelativeLayout>

    <DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView
      android:id="@+id/debug_overlay_view_switch_row"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      app:settingDescription="@string/settings_debug_overlay_view_description"
      app:settingTitle="@string/settings_debug_overlay_view" />


    <View style="@style/Border" />

    <Switch
      android:id="@+id/setting_switch"
      android:checked="true"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" />

  </LinearLayout>

</ScrollView>

こんな感じ

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
public class fragment_settings_holder : IDisposable
{
    public LinearLayout root_view { get; }
    public RelativeLayout language_settings_container { get; }
    public TextView txt_title { get; }
    public TextView setting_description { get; }
    public TextView txt_language { get; }
    public View heads_up_border_top { get; }
    public DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView local_time_switch_row { get; }
    public View heads_up_border_bottom { get; }
    public DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView notification_switch_row { get; }
    public DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView heads_up_switch_row { get; }
    public View heads_up_border { get; }
    public TextView developer_menu_title { get; }
    public TextView developer_menu_tips { get; }
    public DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView debug_overlay_view_switch_row { get; }
    public Switch setting_switch { get; }

    public fragment_settings_holder(View view)
    {
        root_view = view.FindViewById<LinearLayout>(DroidKaigi2017.Droid.Resource.Id.root_view);
        language_settings_container = view.FindViewById<RelativeLayout>(DroidKaigi2017.Droid.Resource.Id.language_settings_container);
        txt_title = view.FindViewById<TextView>(DroidKaigi2017.Droid.Resource.Id.txt_title);
        setting_description = view.FindViewById<TextView>(DroidKaigi2017.Droid.Resource.Id.setting_description);
        txt_language = view.FindViewById<TextView>(DroidKaigi2017.Droid.Resource.Id.txt_language);
        heads_up_border_top = view.FindViewById<View>(DroidKaigi2017.Droid.Resource.Id.heads_up_border_top);
        local_time_switch_row = view.FindViewById<DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView>(DroidKaigi2017.Droid.Resource.Id.local_time_switch_row);
        heads_up_border_bottom = view.FindViewById<View>(DroidKaigi2017.Droid.Resource.Id.heads_up_border_bottom);
        notification_switch_row = view.FindViewById<DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView>(DroidKaigi2017.Droid.Resource.Id.notification_switch_row);
        heads_up_switch_row = view.FindViewById<DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView>(DroidKaigi2017.Droid.Resource.Id.heads_up_switch_row);
        heads_up_border = view.FindViewById<View>(DroidKaigi2017.Droid.Resource.Id.heads_up_border);
        developer_menu_title = view.FindViewById<TextView>(DroidKaigi2017.Droid.Resource.Id.developer_menu_title);
        developer_menu_tips = view.FindViewById<TextView>(DroidKaigi2017.Droid.Resource.Id.developer_menu_tips);
        debug_overlay_view_switch_row = view.FindViewById<DroidKaigi2017.Droid.Views.CustomViews.SettingSwitchRowView>(DroidKaigi2017.Droid.Resource.Id.debug_overlay_view_switch_row);
        setting_switch = view.FindViewById<Switch>(DroidKaigi2017.Droid.Resource.Id.setting_switch);
    }
    public void Dispose()
    {
        root_view.Dispose();
        language_settings_container.Dispose();
        txt_title.Dispose();
        setting_description.Dispose();
        txt_language.Dispose();
        heads_up_border_top.Dispose();
        local_time_switch_row.Dispose();
        heads_up_border_bottom.Dispose();
        notification_switch_row.Dispose();
        heads_up_switch_row.Dispose();
        heads_up_border.Dispose();
        developer_menu_title.Dispose();
        developer_menu_tips.Dispose();
        debug_overlay_view_switch_row.Dispose();
        setting_switch.Dispose();
    }
}

ソリューションに組み込めば少し幸せになれるかもしれない。

実際に組み込んだらこんな感じ ↓


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace Nyanto.ViewSupportTool
{
    class Program
    {
        static Dictionary<string,string> replaceName = new Dictionary<string, string>()
        {
            {"android.support.constraint.ConstraintLayout", "android.support.constraints.ConstraintLayout" }
        };
        static void Main(string[] args)
        {

            var targetProjectPath = args[0];
            var rootNamespace = args[1];
            var resourcePath = Path.Combine(targetProjectPath, "Resources");
            var layoutPath = Path.Combine(resourcePath, "layout");
            var outputFilePath = Path.Combine(resourcePath, "Layout.Designer.cs");
            var files = Directory.GetFiles(layoutPath);
            var sb = new StringBuilder();
            sb.AppendLine("using System;");
            sb.AppendLine("using System.Collections.Generic;");
            sb.AppendLine("using System.Linq;");
            sb.AppendLine("using System.Text;");
            sb.AppendLine("using Android.App;");
            sb.AppendLine("using Android.Content;");
            sb.AppendLine("using Android.OS;");
            sb.AppendLine("using Android.Runtime;");
            sb.AppendLine("using Android.Views;");
            sb.AppendLine("using Android.Widget;");
            sb.AppendLine();
            sb.AppendLine($"namespace {rootNamespace}");
            sb.AppendLine("{");
            foreach (var file in files)
            {
                CreateClass(sb, file, rootNamespace);
            }
            sb.AppendLine("}");

            Console.WriteLine(sb);
            var writer = File.CreateText(outputFilePath);
            writer.Write(sb.ToString());

            writer.Flush();
            writer.Dispose();

            Console.Read();
        }

        static void CreateClass(StringBuilder sb, string filePath, string nameSpaceName)
        {
            var classname = Path.GetFileNameWithoutExtension(filePath) + "_holder";
            var xml = File.ReadAllText(filePath);
            var xdoc = XDocument.Parse(xml);
            var reader = xdoc.CreateReader();
            var controls = new List<Control>();
            while (reader.Read())
            {
                var control = new Control();
                var basename = reader.Name;
                if (replaceName.ContainsKey(basename))
                {
                    basename = replaceName[basename];
                }
                var fileName = string.Join(".", basename.Split('.').Select(x => FirstLetterToUpper(x)));

                control.Name = fileName;

                if (reader.MoveToAttribute("android:id"))
                {
                    control.Id = reader.Value.Split('/')[1];
                    controls.Add(control);
                }
            }

            sb.Append("\t");
            sb.AppendLine($"public class {classname} : IDisposable");
            sb.Append("\t");
            sb.AppendLine("{");

            foreach (var control in controls)
            {
                sb.Append("\t");
                sb.Append("\t");
                sb.Append($"public {control.Name} {control.Id} ");
                sb.Append("{ get; }");
                sb.AppendLine();
            }
            sb.AppendLine();

            sb.Append("\t");
            sb.Append("\t");
            sb.Append($"public {classname}(View view)");
            sb.AppendLine();

            sb.Append("\t");
            sb.AppendLine("\t{");

            foreach (var control in controls)
            {
                sb.Append("\t");
                sb.Append("\t");
                sb.Append("\t");
                sb.Append($"{control.Id} = view.FindViewById<{control.Name}>({nameSpaceName}.Resource.Id.{control.Id});");
                sb.AppendLine();
            }

            sb.Append("\t");
            sb.AppendLine("\t}");

            sb.Append("\t");
            sb.AppendLine($"\tpublic void Dispose()");
            sb.Append("\t");
            sb.AppendLine("\t{");

            foreach (var control in controls)
            {
                sb.Append("\t");
                sb.Append("\t");
                sb.Append("\t");
                sb.Append($"{control.Id}.Dispose();");
                sb.AppendLine();
            }
            sb.Append("\t");
            sb.AppendLine("\t}");

            sb.Append("\t");
            sb.AppendLine("}");
            sb.AppendLine();


        }

        public static string FirstLetterToUpper(string str)
        {
            if (str == null)
                return null;

            if (str.Length > 1)
                return char.ToUpper(str[0]) + str.Substring(1);

            return str.ToUpper();
        }

        private class Control
        {
            public string Name { get; set; }
            public string Id { get; set; }
        }
    }
}

こんな感じにしてtargetsスクリプトでごにょごにょすれば自動的に作成されるようになる。


                var viewAccesor = new view_session_cell_holder(holder.ItemView);


                viewAccesor.root.Clickable = vm.IsSelectable;

                viewAccesor.root.SetBackgroundResource(vm.BackgroundResourceId);

                viewAccesor.categoryBorder.Visibility = vm.IsNormalSession.ToViewStates();
                viewAccesor.categoryBorder.SetBackgroundResource(vm.TopicColorResourceId);
                viewAccesor.img_check.Visibility = vm.IsCheckVisible.Value.ToViewStates();
                viewAccesor.root.Click += (sender, args) => { };
                viewAccesor.root.LongClick += (sender, args) =>
                {
                    var pos = base._recyclerView.GetChildAdapterPosition((View) sender);
                    var viewmodel = Get(pos);
                    viewmodel.CheckCommand.Execute(!viewmodel.IsCheckVisible.Value);
                };
                vm.IsCheckVisible
                    .Skip(1)
                    .ObserveOnUIDispatcher()
                    .Subscribe(x => viewAccesor.img_check.Visibility = x.ToViewStates());

                viewAccesor.txt_time.Text = vm.ShortStartTime;
                viewAccesor.txt_minutes.Text = vm.Minutes;
                viewAccesor.txt_title.Text = vm.Title;
                viewAccesor.txt_title.SetMaxLines(vm.TitleMaxLines);
                viewAccesor.txt_language.Text = vm.LanguageId;
                viewAccesor.txt_language.Visibility = vm.IsLanguageVisible.ToViewStates();
                viewAccesor.txt_speaker_name.Text = vm.SpeakerName;
                viewAccesor.txt_speaker_name.SetMaxLines(vm.SpeakerNameMaxLines);
                viewAccesor.txt_speaker_name.Visibility = vm.IsNormalSession.ToViewStates();

こんな感じでかけて少し幸せ。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1