Привет, Хабр!

Вступление

В этой статье я буду рассказывать о создании своей IDE для Android. Я не нашел какого-либо материала на эту тему. Хочу разделить статью на несколько подстатей, так как информации будет много. Также буду оставлять ссылки на весь материал который я использовал.

Статья предназначена для новичков, ну и для тех кто хочет создать свою IDE для мобилки. Мы будем делать IDE для языка программирования Kotlin, но это не имеет особого значения, так как в проект можно будет добавить любой язык.

Также нам понадобятся библиотеки MaterialDesign и AndroidX, для создания красивого интерфейса, ну они часто уже есть в проектах по умолчанию, А также SoraEditor для создания удобного редактора с оптимизированой подсветкой синтаксиса.

Подготовка

Для начало подключим все нужные библиотеки. Заходим в файл app/build.gradle, и добавляем нужные зависимости:

dependencies {
    implementation("com.google.android.material:material:1.9.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
    
    // sora editor API
    // implementation(platform("io.github.Rosemoe.sora-editor:bom:0.23.2"))
    // implementation 'io.github.Rosemoe.sora-editor:editor'
    // implementation 'io.github.Rosemoe.sora-editor:language-textmate'
    // Временно отключим
}

Пока-что зависимости SoraEditor нам не понадобятся, его мы будем использовать при создании самого редактора.

Также нужно поменять версию Java минимум на 17 для работы SoraEditor. В файле app/build.gradle:

android {
    ...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    ...
}

Также, я отключу viewbinding, так как мне не нравится его использовать, но если вам он нравится, можете оставить.

Создадим класс App он будет наследоваться от Application, он будет хранить различные константы и общие данны, такие как путь к папке с проектами.

Мы будем хранить проекты во внутреннем хранилище, которое будт доступно всем приложениям. Конечно можно хранить и во внешнем, но не виду смысла заморачиваться на этот счет, я всеровно сделаю экспорт проекта в формате ZIP архива.

Пример класса App:

public class App extends Application {
    
    private static Context context;
    
    public static Context getContext() {
        return context;
    }
    
    public static File getProjectsDir() {
        File dir = new File(context.getExternalFilesDir(null), "projects/");
        if(!dir.exists()) dir.mkdirs();
        return dir;
    }
}

Далее в манифесте, в <application> добавляем тег android:name=".App", псли ваш класс находится в другом пакете, то укажите другой пакет. Пример:

<application
  android:name=".App">
  ...
</application>

Создание меню проектов

Создайте активность если вы ее еще не создали, я назову свою ProjectsActivity, и она будет находится в пакете .activitys для большего удобства, там будут находится все активности приложения.

В разметке этой активности, мы должны добавить 2 view, это Toolbar и RecyclerView.

Toolbar - это верхняя панель в приложении, которая является более гибкой версией ActionBar и AppBar, так мы может расположить ее в любом месте, но я уже по привычке его использую, вы можете использовать свой Toolbar.

RecyclerView - это улучшеная версия ListView, которая дополнительна оптимизирована для большого массива данных, так как использует минимальное кол-во элементов которые можно показать на экране, его рекомендуется использовать.

Вот пример разметки:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/toolbar"/>
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/projects"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

Я храню все цвета в файле colors.xml, чтоб можно было поменять в любой момент не бегая по файлам, в также чтобы удобно было делать что-то для другой темы, тоже самое с strings.xml но для языков. Я это написал потому-что многие не соблюдают эти обычаи.

Далее, нужно создать адаптер, а также сам элемент проекта в layout/.

Я создам дирректорию project, там будут все с этим связаные файле. Там создадим класс Project, для удобного использования, там будет хранится название проекта и путь к нему. Пример кода:

public class Project {
    private String name;
    private File path;

    public Project(File path) {
        this.path = path;
        this.name = path.getName();
    }
    
    public Project(String name) {
        this.name = name;
        this.path = new File(App.getProjectsDir(), name);
    }

    public boolean create() {
        File main = new File(path, "main.lua");
        try {
            main.createNewFile();
        } catch(IOException e) {
            e.printStackTrace();
            return false;
        }
        return path.mkdirs();
    }
    
    // Другие методы...
}

Далее создаем адаптер. Создадим класс ProjectsAdapter, и наследуем RecyclerView.Adapter<ProjectsAdapter.VH>. VH это класс который мы должны создать для хранения View в каждом элементе в списке он наследуется от RecyclerView.ViewHolder. В моем будет хранится TextView и ImageView для создания меню с выбором действие, например удалить или переименовать.

В адаптере нужно переопределить несколько методов, это onCreateViewHolder(ViewGroup, int), onBindViewHolder(VH, int) и getItemCount().

onCreateViewHolder - в нес воздается поверхность (ViewHolder).

onBindViewHolder - тут обычно идет остальная настрока каждого элемента списка.

getItemCount - возвращает размер списка с элементам.

Пример адаптера
public class ProjectsAdapter extends RecyclerView.Adapter<ProjectsAdapter.VH> {
    public class VH extends RecyclerView.ViewHolder {
        private View parent;

        public VH(View parent) {
            super(parent);
            this.parent = parent;
            name = parent.findViewById(R.id.name);
            more = parent.findViewById(R.id.more);
        }

        private TextView name;
        private ImageView more;
    }
    
    private List<Project> list;
    private Context context;
    
    public ProjectsAdapter(Context context, List<Project> list) {
        this.context = context;
        this.list = list;
    }
    
    @Override
    public VH onCreateViewHolder(ViewGroup parent, int pos) {
        View view = LayoutInflater.from(context).inflate(R.layout.project_item, parent, false);
        return new VH(view);
    }

    @Override
    public void onBindViewHolder(VH vh, int pos) {
        vh.name = list.get(pos).getName();
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    public void updateList(List<Project> list) {
        this.list = list;
        notifyDataSetChanged(); // говорим адаптеру что весь список изменился
    }
}

В App я добавлю метод getProjects() который будет возвращать List<Project>

public static List<Project> getProjects() {
    List<Project> projects = new ArrayList<>();
        for(File file : getProjectsDir().listFiles()) {
        	if(file.isDirectory()) projects.add(file);
        }
    return projects;
}

В ProjectsActivity в методе onCreate, мы создать адаптер, и применить его к RecyclerVie

Пример
ProjectsAdapter adapter;
RecyclerView rv
    
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    rv = findViewById(R.id.projects);
    adapter = new ProjectsAdapter(this, App.getProjects());
    rv.setLayoutManager(new LinearLayoutManager(this));
    rv.setAdapter(adapter);
}
    
public void updateProjects() {
    adapter.updateList(App.getProjects());
}

Управление проектами реализуем в следующей статье. Если будут вопросы можете задать их в комментариях.

Пока, Хабр! :-)

Комментарии (2)