Wenn Sie eine Tutorial-App oder eine Art App erstellen, die das Programmieren lehrt, dann brauchen Sie ein natives Widget, das den Code nicht nur darstellen, sondern auch beleuchten kann. Ein solches Widget nennen wir Codeview und das ist es, was wir in diesem Tutorial behandeln. Wir sehen uns verschiedene Codeview-Bibliotheken und Beispiele an.
(a). CodeView
Code-Highlighter-Widget.
Hier sind die Eigenschaften dieses Widgets:
- Powered by Highlight.js
- 176 Sprachen und 79 Stile
- Zeilenumbruch
- Sprach-Erkennung
- Zoom (Pinch-Geste)
- Zeilennummer
- Zeilenzahl
- Aktuelle Zeile hervorheben (durch Klicken/Tippen)
- Zeile hervorheben
- Tippen Sie auf die Zeilen (erhalten Sie die Zeilennummer und Ihren Inhalt)
Hier ist eine einfache Demo:
Schritt 1: CodeView installieren
Diese Bibliothek wird in jitpack gehostet, also müssen Sie jitpack als Maven-Url unter dem Abschluss "All Projects" in Ihrer build.gradle auf Projektebene registrieren:
maven { url "https://jitpack.io" }
Dann fügen Sie die Abhängigkeit in der app-level build.gradle hinzu:
implementation 'com.github.tiagohm:CodeView:0.4.0'
Jetzt synchronisieren, um die Bibliothek herunterzuladen und zu installieren.
Schritt 2: CodeView zum Layout hinzufügen
Gehen Sie zu dem xml-Layout, in dem Sie den Codeview rendern wollen und fügen Sie folgendes hinzu:
<br.tiagohm.codeview.CodeView
android:id="@+id/codeView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cv_font_size="14"
app:cv_highlight_line_number="36"
app:cv_show_line_number="true"
app:cv_start_line_number="0"
app:cv_wrap_line="true"
app:cv_zoom_enable="true">
</br.tiagohm.codeview.CodeView>
Schritt 3: Code schreiben
Schreiben Sie nun den Code. Wenn Sie Java verwenden, können Sie zum Beispiel den Codeview wie folgt referenzieren;
mCodeView = (CodeView)findViewById(R.id.codeView);
Oder Sie können Data Binding oder View Binding verwenden, egal ob Sie Java oder Kotlin verwenden. Sie können auch Kotlin-Synthetik verwenden, wenn Sie Kotlin verwenden.
Dann setzen Sie den Highlight-Listener, das Thema, den Code, die Sprache und andere Einstellungen wie Schriftgröße und Zoom-Eigenschaften wie folgt:
mCodeView.setOnHighlightListener(this)
.setOnHighlightListener(this)
.setTheme(Theme.AGATE)
.setCode(JAVA_CODE)
.setLanguage(Language.JAVA)
.setWrapLine(true)
.setFontSize(14)
.setZoomEnabled(true)
.setShowLineNumber(true)
.setStartLineNumber(9000)
.apply();
Hier sind einige andere Methoden:
mCodeView.highlightLineNumber(10);
mCodeView.toggleLineNumber();
mCodeView.getLineCount();
Beispiel
Schauen wir uns nun ein vollständiges Codeview-Beispiel an:
(a). activity_main.xml
Erstellen Sie das Layout für unsere Hauptaktivität und den Codeview dazu:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="br.tiagohm.codeview.app.MainActivity">
<br.tiagohm.codeview.CodeView
android:id="@+id/code_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:cv_font_size="14"
app:cv_highlight_line_number="36"
app:cv_show_line_number="true"
app:cv_start_line_number="0"
app:cv_wrap_line="true"
app:cv_zoom_enable="true">
</br.tiagohm.codeview.CodeView>
</LinearLayout>
(b). MainActivity.java
Unsere einzige Aktivität:
package br.tiagohm.codeview.app;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import br.tiagohm.codeview.CodeView;
import br.tiagohm.codeview.Language;
import br.tiagohm.codeview.Theme;
public class MainActivity extends AppCompatActivity implements CodeView.OnHighlightListener {
private static final String JAVA_CODE = "package com.example.android.bluetoothchat;\n" +
"\n" +
"import android.os.Bundle;\n" +
"import android.support.v4.app.FragmentTransaction;\n" +
"import android.view.Menu;\n" +
"import android.view.MenuItem;\n" +
"import android.widget.ViewAnimator;\n" +
"\n" +
"import com.example.android.common.activities.SampleActivityBase;\n" +
"import com.example.android.common.logger.Log;\n" +
"import com.example.android.common.logger.LogFragment;\n" +
"import com.example.android.common.logger.LogWrapper;\n" +
"import com.example.android.common.logger.MessageOnlyLogFilter;\n" +
"\n" +
"/**\n" +
" * A simple launcher activity containing a summary sample description, sample log and a custom\n" +
" * {@link android.support.v4.app.Fragment} which can display a view.\n" +
" * <p>\n" +
" * For devices with displays with a width of 720dp or greater, the sample log is always visible,\n" +
" * on other devices it's visibility is controlled by an item on the Action Bar.\n" +
" */\n" +
"public class MainActivity extends SampleActivityBase {\n" +
"\n" +
" public static final String TAG = \"MainActivity\";\n" +
"\n" +
" // Whether the Log Fragment is currently shown\n" +
" private boolean mLogShown;\n" +
"\n" +
" @Override\n" +
" protected void onCreate(Bundle savedInstanceState) {\n" +
" super.onCreate(savedInstanceState);\n" +
" setContentView(R.layout.activity_main);\n" +
"\n" +
" if (savedInstanceState == null) {\n" +
" FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();\n" +
" BluetoothChatFragment fragment = new BluetoothChatFragment();\n" +
" transaction.replace(R.id.sample_content_fragment, fragment);\n" +
" transaction.commit();\n" +
" }\n" +
" }\n" +
"\n" +
" @Override\n" +
" public boolean onCreateOptionsMenu(Menu menu) {\n" +
" getMenuInflater().inflate(R.menu.main, menu);\n" +
" return true;\n" +
" }\n" +
"\n" +
" @Override\n" +
" public boolean onPrepareOptionsMenu(Menu menu) {\n" +
" MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);\n" +
" logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);\n" +
" logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);\n" +
"\n" +
" return super.onPrepareOptionsMenu(menu);\n" +
" }\n" +
"\n" +
" @Override\n" +
" public boolean onPrepareOptionsMenu(Menu menu) {\n" +
" MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);\n" +
" logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);\n" +
" logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);\n" +
"\n" +
" return super.onPrepareOptionsMenu(menu);\n" +
" }\n" +
"\n" +
" @Override\n" +
" public boolean onPrepareOptionsMenu(Menu menu) {\n" +
" MenuItem logToggle = menu.findItem(R.id.menu_toggle_log);\n" +
" logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator);\n" +
" logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log);\n" +
"\n" +
" return super.onPrepareOptionsMenu(menu);\n" +
" }\n" +
"\n" +
" @Override\n" +
" public boolean onOptionsItemSelected(MenuItem item) {\n" +
" switch(item.getItemId()) {\n" +
" case R.id.menu_toggle_log:\n" +
" mLogShown = !mLogShown;\n" +
" ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output);\n" +
" if (mLogShown) {\n" +
" output.setDisplayedChild(1);\n" +
" } else {\n" +
" output.setDisplayedChild(0);\n" +
" }\n" +
" supportInvalidateOptionsMenu();\n" +
" return true;\n" +
" }\n" +
" return super.onOptionsItemSelected(item);\n" +
" }\n" +
"\n" +
" /** Create a chain of targets that will receive log data */\n" +
" @Override\n" +
" public void initializeLogging() {\n" +
" // Wraps Android's native log framework.\n" +
" LogWrapper logWrapper = new LogWrapper();\n" +
" // Using Log, front-end to the logging chain, emulates android.util.log method signatures.\n" +
" Log.setLogNode(logWrapper);\n" +
"\n" +
" // Filter strips out everything except the message text.\n" +
" MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter();\n" +
" logWrapper.setNext(msgFilter);\n" +
"\n" +
" // On screen logging via a fragment with a TextView.\n" +
" LogFragment logFragment = (LogFragment) getSupportFragmentManager()\n" +
" .findFragmentById(R.id.log_fragment);\n" +
" msgFilter.setNext(logFragment.getLogView());\n" +
"\n" +
" Log.i(TAG, \"Ready\");\n" +
" }\n" +
"}";
CodeView mCodeView;
private ProgressDialog mProgressDialog;
private int themePos = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCodeView = (CodeView) findViewById(R.id.code_view);
mCodeView.setOnHighlightListener(this)
.setOnHighlightListener(this)
.setTheme(Theme.ARDUINO_LIGHT)
.setCode(JAVA_CODE)
.setLanguage(Language.AUTO)
.setWrapLine(true)
.setFontSize(14)
.setZoomEnabled(true)
.setShowLineNumber(true)
.setStartLineNumber(1)
.apply();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.change_theme_action:
setHighlightTheme(++themePos);
return true;
case R.id.show_line_number_action:
mCodeView.toggleLineNumber();
break;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onStartCodeHighlight() {
mProgressDialog = ProgressDialog.show(this, null, "Carregando...", true);
}
@Override
public void onFinishCodeHighlight() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
Toast.makeText(this, "line count: " + mCodeView.getLineCount(), Toast.LENGTH_SHORT).show();
}
@Override
public void onLanguageDetected(Language language, int relevance) {
Toast.makeText(this, "language: " + language + " relevance: " + relevance, Toast.LENGTH_SHORT).show();
}
@Override
public void onFontSizeChanged(int sizeInPx) {
Log.d("TAG", "font-size: " + sizeInPx + "px");
}
@Override
public void onLineClicked(int lineNumber, String content) {
Toast.makeText(this, "line: " + lineNumber + " html: " + content, Toast.LENGTH_SHORT).show();
}
private void setHighlightTheme(int pos) {
mCodeView.setTheme(Theme.ALL.get(pos)).apply();
Toast.makeText(this, Theme.ALL.get(pos).getName(), Toast.LENGTH_SHORT).show();
}
}
Quellcode finden Sie hier.
Referenz
Quellcode-Referenz finden hier.
(b). CodeView-Android
Zeigt Code mit Syntaxhervorhebung in nativer Weise an.
CodeView enthält 3 Kernteile, um die notwendige Logik zu implementieren:
- CodeView & zugehöriger abstrakter Adapter zur Bereitstellung von Optionen und Anpassungen (siehe unten).
- Für die Hervorhebung verwendet es CodeHighlighter, es hebt Ihren Code hervor und gibt formatierte Inhalte zurück. Es basiert auf Google Prettify und deren Java-Implementierung & fork.
- CodeClassifier versucht zu definieren, welche Sprache im Codeschnipsel dargestellt wird. Er basiert auf dem Naive Bayes Classifier, der auf einer Open-Source-Implementierung basiert, die ich in Kotlin umgeschrieben habe. Es ist nicht notwendig, direkt mit dieser Klasse zu arbeiten und Sie müssen nur den Anweisungen unten folgen. (Experimentelles Modul, kann nicht richtig funktionieren!)
Schritt 1: Installieren
Diese Bibliothek wird auch in jitpack gehostet, also fügen Sie jitpack in Ihrer root level gradle Datei hinzu:
maven { url "https://jitpack.io" }
Dann installieren Sie sie:
implementation 'com.github.kbiakov:CodeView-Android:1.3.2'
Schritt 2: CodeView Layout hinzufügen
Fügen Sie Codeview zu Ihrem xml-Layout hinzu:
<io.github.kbiakov.codeview.CodeView
android:id="@+id/code_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Schritt 3: Code schreiben
Tranin Code Classifier
Wenn Sie den Code Classifier zur automatischen Erkennung der Sprache verwenden möchten, fügen Sie ihn einfach in Ihre Application.java ein:
// train classifier on app start
CodeProcessor.init(this);
Reference CodeView
Das heißt, wenn Sie noch den alten findViewById Stil verwenden:
CodeView codeView = (CodeView) findViewById(R.id.code_view);
Set Code
Sie können jetzt Code auf Ihre Codeview mit automatischer Spracherkennung setzen:
// auto language recognition
codeView.setCode(getString(R.string.listing_js));
Sie können die Sprache auch explizit angeben:
// will work faster!
codeView.setCode(getString(R.string.listing_py), "py");
Thema ändern
Sie können das Theme wie folgt ändern:
codeView.getOptions().setTheme(ColorTheme.SOLARIZED_LIGHT);
Weitere Anpassungen:
codeView.setOptions(Options.Default.get(this)
.withLanguage("python")
.withCode(R.string.listing_py)
.withTheme(ColorTheme.MONOKAI));
Und .
ColorThemeData myTheme = ColorTheme.SOLARIZED_LIGHT.theme()
.withBgContent(android.R.color.black)
.withNoteColor(android.R.color.white);
codeView.getOptions().setTheme(myTheme);
Schriftart einstellen
Sie können Ihre eigene Schriftart einstellen:
codeView.getOptions().withFont(Font.Consolas);
Beispiel
Ein komplettes Beispiel finden Sie hier
Referenz
Weitere Dokumentation finden Sie hier