Si estás creando una aplicación para un tutorial o un tipo de aplicación que enseñe a codificar, entonces te vendría bien un widget nativo capaz no sólo de renderizar el código sino también de iluminarlo. Tal widget es lo que llamamos codeview y eso es lo que cubrimos en este tutorial. Veremos varias librerías de codeview y ejemplos.

(a). CodeView

Widget resaltador de código.

Estas son las características de este widget:

  • Desarrollado por Highlight.js
  • 176 idiomas y 79 estilos
  • Línea de envoltura
  • Detección de idioma
  • Zoom (gesto de pellizco)
  • Número de líneas
  • Recuento de líneas
  • Resaltar la línea actual (por clic/toque)
  • Resaltar línea
  • Evento de toque de líneas (obtener el número de línea y su contenido)

Aquí está la demostración simple:

Paso 1: Instalar CodeView

Esta librería está alojada en jitpack por lo que necesitas registrar jitpack como url de maven bajo el cierre de todos los proyectos en tu build.gradle a nivel de proyecto:

 maven { url "https://jitpack.io" }

Luego añadir la dependencia en el build.gradle a nivel de aplicación:

implementation 'com.github.tiagohm:CodeView:0.4.0'

Ahora sincronizar para descargar e instalar la biblioteca.

Paso 2: Añadir CodeView al Layout

Ve al layout xml donde quieres renderizar el codeview y luego añade lo siguiente:

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

Paso 3: Escribir el código

Ahora ve y escribe el código. Si estás usando java puedes, por ejemplo, referenciar el codeview de la siguiente manera;

mCodeView = (CodeView)findViewById(R.id.codeView);

O puedes usar data binding o view binding ya sea que estés usando java o kotlin. También puede utilizar kotlin sintéticos si usted está usando kotlin.

A continuación, establezca el oyente de resaltado, el tema, el código, el idioma y otros ajustes como el tamaño de la fuente y las propiedades de zoom de la siguiente manera:

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();

Aquí hay algunos otros métodos:

mCodeView.highlightLineNumber(10);
mCodeView.toggleLineNumber();
mCodeView.getLineCount();

Ejemplo

Veamos ahora un ejemplo completo de codeview:

(a). activity_main.xml

Crear el layout de nuestra actividad principal y el codeview a la misma:

<?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

Nuestra única actividad:

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();
    }
}

Encuentra el código fuente aquí.

Referencia

Encuentra la referencia del código fuente aquí.

(b). CodeView-Android

Muestra el código con destellos de resaltado de sintaxis de forma nativa.

CodeView contiene 3 partes centrales para implementar la lógica necesaria:

  1. CodeView y el adaptador abstracto relacionado para proporcionar opciones y personalización (ver abajo).
  2. Para resaltar utiliza CodeHighlighter, que resalta tu código y devuelve el contenido formateado. Está basado en Google Prettify y su implementación en Java y fork.
  3. CodeClassifier trata de definir qué lenguaje se presenta en el fragmento de código. Está construido usando Naive Bayes classifier sobre la implementación de código abierto encontrada, que reescribí en Kotlin. No hay necesidad de trabajar con esta clase directamente y sólo debe seguir las instrucciones a continuación. (Módulo experimental, puede que no funcione correctamente)

Android CodeView Example

Paso 1: Instalarlo

Esta librería también está alojada en jitpack por lo que debes añadir jitpack en tu archivo gradle de nivel raíz:

 maven { url "https://jitpack.io" }

Luego instálala:

implementation 'com.github.kbiakov:CodeView-Android:1.3.2'

Paso 2: Añadir CodeView Layout

Añade codeview a tu layout xml:

<io.github.kbiakov.codeview.CodeView
    android:id="@+id/code_view"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

Paso 3: Escribir el código

Clasificador de código de tren

Si quieres usar el clasificador de código para el reconocimiento automático del lenguaje sólo tienes que añadirlo a tu Application.java:

// train classifier on app start
CodeProcessor.init(this);

Vista de código de referencia

Esto es si usted todavía está usando el viejo estilo findViewById:

CodeView codeView = (CodeView) findViewById(R.id.code_view);

Set Code

Ahora puedes establecer código en tu codeview con detección automática de idioma:

// auto language recognition
codeView.setCode(getString(R.string.listing_js));

También puede especificar explícitamente el idioma:

// will work faster!
codeView.setCode(getString(R.string.listing_py), "py");

Cambiar tema

Puede cambiar el tema de la siguiente manera:

codeView.getOptions().setTheme(ColorTheme.SOLARIZED_LIGHT);

Más personalizaciones:

codeView.setOptions(Options.Default.get(this)
    .withLanguage("python")
    .withCode(R.string.listing_py)
    .withTheme(ColorTheme.MONOKAI));

Y

ColorThemeData myTheme = ColorTheme.SOLARIZED_LIGHT.theme()
    .withBgContent(android.R.color.black)
    .withNoteColor(android.R.color.white);

codeView.getOptions().setTheme(myTheme);

Configuración de la fuente

Puedes establecer tu propio tipo de letra:

codeView.getOptions().withFont(Font.Consolas);

Ejemplo

Encuentre el ejemplo completo aquí

Referencia

Encuentre más documentación aquí

Categorizado en:

Etiquetado en: