Android

Source code analysis of default background color for View focus state

Author : Lee
Published Time : 2025-11-04

We will analyze the mechanism of generating focus background colors from the source code level and provide a complete Java solution. The following analysis is based on the Android Framework source code (API 30).


Source code analysis of focus background color generation mechanism

1.Focus state triggers background color:

// View.java
public void setFocused(boolean focused) {
    if (focused) {
        // Refresh Drawable state when the focus state changes
        refreshDrawableState();
        // Request to redraw
        invalidate(true);
    }
}

2. Drawable status update:

// View.java
protected void drawableStateChanged() {
    final Drawable d = mBackground;
    if (d != null && d.isStateful()) {
        // Apply the current view state to the background Drawable
        d.setState(getDrawableState());
    }
}

protected int[] getDrawableState() {
    if (mFocused) {
        // Add State-FOCUS-ED to the state set
        return StateSet.get(StateSet.VIEW_STATE_FOCUSED);
    }
    return StateSet.WILD_CARD;
}

3. Default focus background implementation:

// Theme.java
public Drawable getDefaultFocusHighlightDrawable() {
    // Get default focus highlight Drawable from theme
    return mResources.getDrawable(
        com.android.internal.R.drawable.default_focused_highlight,
        mTheme
    );
}

4. Focus background drawing process:

// View.java
public void draw(Canvas canvas) {
    // Step 1: Draw the background
    drawBackground(canvas);
    
    // Step 2: If it is a focus view, draw a focus highlight
    if (isFocused()) {
        drawDefaultFocusHighlight(canvas);
    }
}

private void drawDefaultFocusHighlight(Canvas canvas) {
    if (mDefaultFocusHighlight != null) {
        // Draw default focus highlight
        mDefaultFocusHighlight.draw(canvas);
    }
}


Complete solution (Java code)

import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.view.View;
import android.widget.TextView;
import com.google.android.material.tabs.TabLayout;

public class FocusBackgroundFixer {

    // Option 1: Disable default focus highlighting globally (API 26+)
    public static void disableDefaultFocusHighlight(View view) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // Directly turn off system level focus highlighting drawing
            view.setDefaultFocusHighlightEnabled(false);
        }
    }

    // Option 2: Custom Drawable state replacement (compatible with all versions)
    public static void setCustomFocusDrawable(View view) {
        // Create a transparent state Drawable
        StateListDrawable drawable = new StateListDrawable();
        
        // Use transparent Drawable for focus state
        drawable.addState(
            new int[]{android.R.attr.state_focused}, 
            new ColorDrawable(Color.TRANSPARENT)
        );
        
        // default state
        drawable.addState(
            new int[]{}, 
            view.getBackground() // Keep the original background
        );
        
        view.setBackground(drawable);
    }

    // Option 3: Dynamic interception of focus background drawing
    public static void interceptFocusDrawing(View view) {
        view.addOnLayoutChangeListener((v, left, top, right, bottom, 
                                       oldLeft, oldTop, oldRight, oldBottom) -> {
            // Clear focus status marker
            v.setSelected(false);
            v.setActivated(false);
            
            // Remove focus related Drawable
            if (v.getBackground() != null) {
                v.getBackground().setState(StateSet.WILD_CARD);
            }
        });
    }

    // Special processing for TextView (ticker scene)
    public static void fixMarqueeTextView(TextView textView) {
        // Retain focus ability (ticker requires focus)
        textView.setFocusable(true);
        textView.setFocusableInTouchMode(true);
        
        // Option 1: Disable default highlighting
        disableDefaultFocusHighlight(textView);
        
        // Option 2: Transparent Background Coverage
        textView.setBackgroundColor(Color.TRANSPARENT);
        
        // Prevent background repainting
        textView.setWillNotDraw(true);
    }

    // Focus repair for TabLayout
    public static void fixTabLayoutFocus(TabLayout tabLayout) {
        // Traverse all tabs
        for (int i = 0; i < tabLayout.getTabCount(); i++) {
            TabLayout.Tab tab = tabLayout.getTabAt(i);
            if (tab != null) {
                View tabView = tab.view;
                
                // Disable default focus highlighting
                disableDefaultFocusHighlight(tabView);
                
                // Custom focus processing
                tabView.setOnFocusChangeListener((v, hasFocus) -> {
                    if (hasFocus) {
                        // Custom logic for focus state (no background changes)
                        v.setBackgroundResource(android.R.color.transparent);
                    }
                });
                
                // Clear the existing focus state
                tabView.clearFocus();
            }
        }
        
        // TabLayout overall focus control
        tabLayout.setFocusable(false);
        tabLayout.setFocusableInTouchMode(false);
    }

    // Application scenario integration
    public static void applyLoginScenarioFix(TextView marqueeView, TabLayout tabLayout) {
        // Fix floating window TextView
        fixMarqueeTextView(marqueeView);
        
        // Fix TabLayout
        fixTabLayoutFocus(tabLayout);
        
        // Global Focus Strategy
        marqueeView.post(() -> {
            // Proactively shift focus to a safe area
            marqueeView.clearFocus();
            tabLayout.clearFocus();
        });
    }
}


Key implementation principles

1. System mechanism coverage:

// Directly turn off the focus, highlight and draw the pipeline
view.setDefaultFocusHighlightEnabled(false);

This method will set mDefault FocusHighlight() to null and skip the drawDefault FocusHighlight() process

2. Drawable state deception:

// Create transparent focus state
drawable.addState(new int[]{android.R.attr.state_focused}, 
                new ColorDrawable(Color.TRANSPARENT));

When the system queries the focus status, it returns transparent Drawable

3. Focus event interception:

view.setOnFocusChangeListener((v, hasFocus) -> {
    if (hasFocus) {
        // Immediately reset the background status
        v.setBackgroundResource(android.R.color.transparent);
    }
});

4. Drawing process optimization:

// Setting will not trigger redraw
textView.setWillNotDraw(true);

Avoid unnecessary redrawing triggered by focus changes


Recommended solutions for different scenarios

sceneRecommended SolutionPrinciple Description
Suspended Window TextViewfixMarqueeTextView()Retain focus ability but disable visual feedback
TabLayoutfixTabLayoutFocus()Tab by Tab Processing to Avoid Internal State Conflicts
Jump scene after password loginapplyLoginScenarioFix()Combination scheme+focus reset
Low version compatibility (API<26)setCustomFocusDrawable()Drawable Coverage Scheme for Status


Advanced Debugging Techniques

// Focus state tracker
view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
    @Override
    public void onViewAttachedToWindow(View v) {
        // Print focus path
        logFocusPath(v);
    }
    
    private void logFocusPath(View v) {
        StringBuilder path = new StringBuilder();
        View current = v;
        while (current != null) {
            path.insert(0, current.getClass().getSimpleName() + 
                        " [F:" + current.hasFocus() + "] -> ");
            current = (current.getParent() instanceof View) ? 
                     (View) current.getParent() : null;
        }
        Log.d("FocusPath", path.toString());
    }
});

These solutions directly solve the problem from the source code level of the Android focus mechanism, retaining necessary focus functions (such as scrolling tickers) while eliminating visual background color anomalies. In practical applications, it should be combined and used according to specific scenarios, and the repair logic should be triggered in onResume() and onWindowFocusChanged().