Giter VIP home page Giter VIP logo

android-customkeyboard's Introduction

Android-CustomKeyboard

Fully customizable Android keyboard written in Kotlin.

Deprecated

This package is no longer maintained. I'm no longer an Android developer and have not committed any code in a few years now. Please feel free to fork this project and make improvements as needed.

Prerequisites

Make sure you have a version of Android Studio installed that supports Kotlin (3+ should be fine).

Running the Demo

Just download the project, open it in Android Studio, connect to a virtual or physical device, and run it! There shouldn't be any further configuration required (unless you need to download build tools, etc., but Android Studio should prompt you to do that).

Below are GIF's of the functionality in both ladscape and portrait. Notice that in both orientations the keyboard takes up the full screen width, and the button widths change (they are a percentage of the screen width). That is because it extends the ResizableRelativeLayout. Additionally, the component responsible for the expansion and collapse of the keyboard is the ExpandableView. Please take a look at their documentation for more detail.

    

Why I Made It

The Android system keyboard API is limited and difficult to work with. I spent many hours researching different ways to gain full control of the keyboard, and ended up piecing together a few different approaches and adding some of my own flavor to it. I hope that I can save somebody else a lot of time and headache.

How It Works

The CustomKeyboardView can be injected with any keyboard layout and controller. All you need to do is create an EditText, pass it to the CustomKeyboardView, and indicate what keyboard type it should be using. Below is the entire MainActivity demo class:

class MainActivity : AppCompatActivity() {
    private lateinit var keyboard: CustomKeyboardView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val numberField: EditText = findViewById(R.id.testNumberField)
        val numberDecimalField: EditText = findViewById(R.id.testNumberDecimalField)
        val qwertyField: EditText = findViewById(R.id.testQwertyField)

        keyboard = findViewById(R.id.customKeyboardView)
        keyboard.registerEditText(CustomKeyboardView.KeyboardType.NUMBER, numberField)
        keyboard.registerEditText(CustomKeyboardView.KeyboardType.NUMBER_DECIMAL, numberDecimalField)
        keyboard.registerEditText(CustomKeyboardView.KeyboardType.QWERTY, qwertyField)

        val switchActivitiesButton: Button = findViewById(R.id.switchActivitiesButton)
        switchActivitiesButton.setOnClickListener {
            startActivity(Intent(this@MainActivity, AdvancedFeaturesActivity::class.java))
        }
    }

    override fun onBackPressed() {
        if (keyboard.isExpanded) {
            keyboard.translateLayout()
        } else {
            super.onBackPressed()
        }
    }
}

The EditText's are stored in a map by the CustomKeyboardView:

private val keyboards = HashMap<EditText, KeyboardLayout?>()

As you can see, they are mapped to their KeyboardLayout, which stores its own controller. This process is shown below:

    fun registerEditText(type: KeyboardType, field: EditText) {
        field.setRawInputType(InputType.TYPE_CLASS_TEXT)
        field.setTextIsSelectable(true)
        field.showSoftInputOnFocus = false
        field.isSoundEffectsEnabled = false
        field.isLongClickable = false

        val inputConnection = field.onCreateInputConnection(EditorInfo())
        keyboards[field] = createKeyboardLayout(type, inputConnection)
        ...
    }
    
    private fun createKeyboardLayout(type: KeyboardType, ic: InputConnection): KeyboardLayout? {
        when(type) {
            KeyboardType.NUMBER -> {
                return NumberKeyboardLayout(context, createKeyboardController(type, ic))
            }
            KeyboardType.NUMBER_DECIMAL -> {
                return NumberDecimalKeyboardLayout(context, createKeyboardController(type, ic))
            }
            KeyboardType.QWERTY -> {
                return QwertyKeyboardLayout(context, createKeyboardController(type, ic))
            }
            else -> return@createKeyboardLayout null // this should never happen
        }
    }

    private fun createKeyboardController(type: KeyboardType, ic: InputConnection): KeyboardController? {
        when(type) {
            KeyboardType.NUMBER -> {
                return DefaultKeyboardController(ic)
            }
            KeyboardType.NUMBER_DECIMAL -> {
                return NumberDecimalKeyboardController(ic)
            }
            KeyboardType.QWERTY -> {
                return DefaultKeyboardController(ic)
            }
            else -> return@createKeyboardController null // this should never happen
        }
    }

You might also notice that the CustomKeyboardView is currently using some very basic controllers, so why would we separate the controller logic from the layout? Because more complicated controllers may be needed in the future. This architecture allows for more complex keyboard layouts to be created. For example, what if we need to create a keyboard that handles latitudes. That can get pretty complicated. Not only do we have to consider that latitudes can only span between S 90.0000 and N 90.0000 degrees, but what if we want to represent those values in degrees and minutes, or degrees and minutes and seconds, or whatever format the user chooses? The architecture might be a little overkill for simple keyboards, but it leaves open the possibility to create any keyboard we may need to in the future without any significant changes to the architecture.

Advanced Use

The CustomKeyboardView is capable of auto registering all EditText's within a ViewGroup. Just pass any ViewGroup to the autoRegisterEditTexts method of any CustomKeyboardView instance, and it will recursively search the View tree for EditText's, check their type, and automatically bind to their InputConnection's.

Additionally, there is a CustomTextField component. This component is a simple extension of the Android EditText component. It auto sets simple settings such as background color, max characters, and text size. The most important addition to this extension is the keyboard type property. You can create this component programmaticaly, set it's type, and pass it's containing ViewGroup to the CustomKeyboardView to auto bind to it's InputConnection. This allows you to create keyboard types that are not recognized by Android, set the keyboard type of this new component, and have that type auto recognized by the CustomKeyboardView. Note: The CustomTextField is a very simple component, and like the custom keyboard's in this repository, it's expected that you'll override their attributes to fit your project's needs.

Take a look at the AdvancedFeaturesActivity.kt class to see examples of advanced use.

Next Steps

Add the CustomKeyboardView to any (and hopefully all :) of your projects, add any layout or controllers you'd like, modify the UI in any way that fits your needs, and enjoy!

Dependencies

License

This project is licensed under the MIT License - see the LICENSE file for details

android-customkeyboard's People

Contributors

donbrody avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

android-customkeyboard's Issues

Error using actual AndroidStudio 3.4

Dear DonBrody,

using the newest AndroidStudio 3.4
"""
Android Studio 3.4
Build #AI-183.5429.30.34.5452501, built on April 10, 2019
JRE: 1.8.0_152-release-1343-b01 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
macOS 10.14.4
"""
the following error + warning occurred:

"""
ERROR: The Android Gradle plugin supports only Kotlin Gradle plugin version 1.3.10 and higher.
The following dependencies do not satisfy the required version:
root project 'Android-CustomKeyboard' -> org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.30
Affected Modules: app

WARNING: API 'variant.getJavaCompile()' is obsolete and has been replaced with 'variant.getJavaCompileProvider()'.
It will be removed at the end of 2019.
For more information, see https://d.android.com/r/tools/task-configuration-avoidance.
To determine what is calling variant.getJavaCompile(), use -Pandroid.debug.obsoleteApi=true on the command line to display more information.
Affected Modules: app
"""

My app/build.gradle that lead to the error looks like this (within the triple quotes):

"""
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 26
defaultConfig {
applicationId "com.donbrody.customkeyboard"
minSdkVersion 23
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
"""
Here it appeared that the dependency:
implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
was the culprit.

Any help how to correct this would be greatly appreciated.
Thank you
Fridemar
PS:
Thank you for contributing the custom keyboard. My use case was a small
customized "NUM" kb with digits,'+' and '-', and a ENTER for inputting a string "xyz.." where x,y or z
can be digits or embedded sign-chars, e.g. like in "912-32+34".

Using the standard attribute for an editfield >android:inputType="numberSigned"<
doesn't allow to type interspersed signs +,-. The system suppresses it.

I figured out the following solution for the gradle dependency files:

"""
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
ext.kotlin_version = '1.3.30'
repositories {
google()
jcenter()

}
dependencies {
    classpath 'com.android.tools.build:gradle:3.4.0'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}

}

allprojects {
repositories {
google()
jcenter()

}

}

task clean(type: Delete) {
delete rootProject.buildDir
}
"""

On module level I used:

"""
...

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

"""

RelativeLayout

Hello,
I see that you have extended RelativeLayout to create the ResizableRelativeLayout class, and then extended that to create the ExpandableView class which you based your keyboard class on. Can I ask what issues you've found with the original RelativeLayout class that prompted you to override and change it?

How to use this with InputMethodService?

As I can see this is pretty useful for In-App keyboard by just registering the Edittext to CustomKeyboadView but I tried using this with InputmethodService. Displays nothing. Can you provide some way to do that? That would be very helpful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.