القائمة الرئيسية

الصفحات

كيفية برمجة تطبيق مرآة احترافي على اندرويد ستوديو | Java

How to Code Professional Mirror App on Android Studio Java، How to Code Professional Mirror App on Android Studio | Java، مرآة ذكية بالكاميرا الأمامية، عرض مباشر بالكاميرا الأمامية،  مرآة مقلوبة تلقائيًا، مؤثرات تجميل للوجه، تصوير سريع وسهل، واجهة عصرية بتقنيات Material Design، برمجة تطبيق مرآة بسيطة باستخدام كاميرا الهاتف،  تطبيق مرآة ذكي للأندرويد، Mirror app Android 2025، استخدام الكاميرا الأمامية كمرايا، تطبيق تجميل بالكاميرا، مرآة عاكسة بتقنية Material 3، أفضل تطبيق مرآة احترافي، تطبيق مرآة أندرويد، Mirror App with Camera Android، استخدام الكاميرا الأمامية، Android Camera2 API، واجهة مرآة بتصميم احترافي، برمجة تطبيق مرآة Java، نشر تطبيق كاميرا على Google Play، كيفية برمجة تطبيق مرآة احترافي على اندرويد ستوديو | Java، مرآة عاكسة بتقنية Material 3، Mirror app Android 2025، Android Camera2 API،




كيفية برمجة تطبيق مرآة احترافي على اندرويد ستوديو | Java


في عالم تطوير تطبيقات الأندرويد المتنامي، تزداد الحاجة إلى تطبيقات بسيطة 
وعملية تلبي احتياجات المستخدمين اليومية. يعتبر تطبيق المرآة مثالاً ممتازاً على
 هذه الفئة، حيث يحول كاميرا الهاتف الأمامية إلى مرآة فورية. هذا المقال سيوضح
 لك كيفية برمجة تطبيق مرآة احترافي باستخدام أندرويد ستوديو ولغة جافا، مع التركيز
 على تحسين الأداء وتقديم تجربة مستخدم سلسة. سنتناول الخطوات الأساسية لبناء تطبيق
 كاميرا يمكن الاعتماد عليه، بدءًا من تصميم الواجهة ووصولاً إلى وظائف الكاميرا المتقدمة.


خطوات برمجة تطبيق مرآة احترافي باستخدام كاميرا الهاتف 


إن بناء تطبيق مرآة احترافي يتطلب فهمًا جيدًا لـواجهات برمجة تطبيقات
 الكاميرا في أندرويد وأفضل الممارسات في تصميم تطبيقات الأندرويد. 
يهدف هذا الدليل إلى تبسيط عملية تطوير تطبيق المرآة، موفرًا خارطة طريق واضحة 
لمساعدتك على إنشاء تطبيق كاميرا فعال وموثوق. سنركز على البرمجة بلغة
 جافا داخل بيئة أندرويد ستوديو، مع توضيح المفاهيم الأساسية للكاميرا 2 API 
وكيفية معالجة بث الفيديو لتقديم وظيفة المرآة الحقيقية.


1.  التحضير والبيئة
- المتطلبات:
Android Studio (آخر إصدار)
لغة البرمجة: Java أو Kotlin (سنستخدم Java هنا)
API Level: 21 أو أعلى (Camera2 API)
صلاحيات الكاميرا .

2.  إنشاء المشروع
الخطوات:
افتح Android Studio
File > New Project > Empty Activity
اختر لغة Java ، سَمِّ المشروع: SmartMirrorApp

3. إضافة الصلاحيات في AndroidManifest.xml
xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="true" />
    <uses-feature android:name="android.hardware.camera.front" android:required="false" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />

<application
    android:usesCleartextTraffic="true"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar">
    ...
</application>
--

4.  تصميم واجهة المستخدم activity_main.xml
xml




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <SurfaceView
        android:id="@+id/camera_preview_surface"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageButton
        android:id="@+id/toggle_camera_button"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_margin="16dp"
        android:src="@android:drawable/ic_menu_camera"
        android:background="?attr/selectableItemBackgroundBorderless"
        android:contentDescription="Toggle Camera"
        android:scaleType="fitCenter"
        android:padding="8dp"/>

    </RelativeLayout>


--

5. تهيئة الكاميرا (Camera2 API) في MainActivity.java

الخطوات:
استخدام TextureView لعرض الكاميرا، فتح الكاميرا الأمامية،
عرض الفيديو بدون التقاط صورة.
java




import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView; // بديل لـ SurfaceView إذا كنت تستخدم TextureView
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CAMERA_PERMISSION = 200;
    private static final String TAG = "MirrorApp";

    private SurfaceView cameraPreviewSurface;
    private SurfaceHolder surfaceHolder;

    private CameraDevice cameraDevice;
    private CameraCaptureSession cameraCaptureSession;
    private CaptureRequest.Builder captureRequestBuilder;
    private Size imageDimension;
    private ImageReader imageReader;

    private Handler mBackgroundHandler;
    private HandlerThread mBackgroundThread;

    private String cameraId;
    private int currentCameraFacing = CameraCharacteristics.LENS_FACING_FRONT; // الكاميرا الأمامية افتراضياً

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        cameraPreviewSurface = findViewById(R.id.camera_preview_surface);
        surfaceHolder = cameraPreviewSurface.getHolder();
        surfaceHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder holder) {
                // عند إنشاء السطح، نبدأ بفتح الكاميرا
                checkCameraPermissions();
            }

            @Override
            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
                // يمكن هنا تعديل أبعاد العرض إذا تغير حجم السطح
            }

            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
                // عند تدمير السطح، نغلق الكاميرا
                closeCamera();
            }
        });

        ImageButton toggleCameraButton = findViewById(R.id.toggle_camera_button);
        toggleCameraButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                toggleCamera();
            }
        });
    }

    // --- إدارة دورة حياة التطبيق والخيوط الخلفية ---

    @Override
    protected void onResume() {
        super.onResume();
        startBackgroundThread();
        if (cameraPreviewSurface.isAvailable()) {
            checkCameraPermissions();
        } else {
            // إذا لم يكن السطح جاهزًا، فإن callback: surfaceCreated سيتم استدعاؤها لاحقًا
        }
    }

    @Override
    protected void onPause() {
        closeCamera();
        stopBackgroundThread();
        super.onPause();
    }

    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }

    private void stopBackgroundThread() {
        if (mBackgroundThread != null) {
            mBackgroundThread.quitSafely();
            try {
                mBackgroundThread.join();
                mBackgroundThread = null;
                mBackgroundHandler = null;
            } catch (InterruptedException e) {
                Log.e(TAG, "Error stopping background thread", e);
            }
        }
    }

    // --- إدارة الأذونات ---

    private void checkCameraPermissions() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED) {
            openCamera();
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.CAMERA},
                    REQUEST_CAMERA_PERMISSION);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CAMERA_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                openCamera();
            } else {
                Toast.makeText(this, "Camera permission is required to use this app.", Toast.LENGTH_LONG).show();
                finish();
            }
        }
    }

    // --- فتح الكاميرا وتهيئة الإعدادات ---

    private void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            // البحث عن الكاميرا الأمامية أو الخلفية بناءً على currentCameraFacing
            for (String cameraId : manager.getCameraIdList()) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing == currentCameraFacing) {
                    this.cameraId = cameraId;
                    StreamConfigurationMap map = characteristics.get(
                            CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                    if (map == null) {
                        continue;
                    }
                    // اختيار أفضل حجم لعرض الكاميرا
                    imageDimension = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                            cameraPreviewSurface.getWidth(), cameraPreviewSurface.getHeight());

                    // يمكنك أيضًا إعداد ImageReader لالتقاط الصور الثابتة هنا
                    imageReader = ImageReader.newInstance(imageDimension.getWidth(), imageDimension.getHeight(),
                            ImageFormat.JPEG, 1);
                    // imageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);

                    break;
                }
            }

            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                // يجب أن تكون الأذونات قد تم التعامل معها بالفعل بواسطة checkCameraPermissions
                return;
            }
            manager.openCamera(cameraId, stateCallback, mBackgroundHandler);

        } catch (CameraAccessException e) {
            Log.e(TAG, "Camera Access Exception", e);
        }
    }

    private void closeCamera() {
        if (cameraCaptureSession != null) {
            cameraCaptureSession.close();
            cameraCaptureSession = null;
        }
        if (cameraDevice != null) {
            cameraDevice.close();
            cameraDevice = null;
        }
        if (imageReader != null) {
            imageReader.close();
            imageReader = null;
        }
    }

    private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            Log.e(TAG, "Camera Opened");
            cameraDevice = camera;
            createCameraPreview(); // بمجرد فتح الكاميرا، نبدأ المعاينة
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            Log.e(TAG, "Camera Disconnected");
            camera.close();
            cameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            Log.e(TAG, "Camera Error: " + error);
            camera.close();
            cameraDevice = null;
            Toast.makeText(MainActivity.this, "Camera error: " + error, Toast.LENGTH_SHORT).show();
            finish();
        }
    };

    // --- اختيار الحجم الأمثل للكاميرا (مساعد) ---
    private static Size chooseOptimalSize(Size[] choices, int width, int height) {
        // جمع الأحجام المدعومة التي هي أكبر من أو تساوي حجم المعاينة
        // والحفاظ على نسبة العرض إلى الارتفاع
        Size bestSize = choices[0];
        long minDiff = Long.MAX_VALUE;

        for (Size option : choices) {
            long diff = Math.abs(option.getWidth() * option.getHeight() - width * height);
            if (diff < minDiff) {
                minDiff = diff;
                bestSize = option;
            } else if (diff == minDiff && option.getWidth() * option.getHeight() > bestSize.getWidth() * bestSize.getHeight()) {
                // إذا كانت الفروق متساوية، اختر الأكبر
                bestSize = option;
            }
        }
        return bestSize;
    }

    // --- تبديل الكاميرا (أمامية/خلفية) ---
    private void toggleCamera() {
        closeCamera(); // أغلق الكاميرا الحالية
        currentCameraFacing = (currentCameraFacing == CameraCharacteristics.LENS_FACING_FRONT) ?
                CameraCharacteristics.LENS_FACING_BACK : CameraCharacteristics.LENS_FACING_FRONT;
        openCamera(); // افتح الكاميرا الجديدة
    }

    // ... (تكملة الكود في الأقسام التالية)
}


--




* عرض فيديو الكاميرا المباشر :

بعد فتح الكاميرا، نقوم بإنشاء CaptureSession وCaptureRequest 
لربط بث الكاميرا بـ SurfaceView.
الكود البرمجي في MainActivity.java (تكملة للكود السابق):

Java




// ... (تكملة من الكود السابق)

    private void createCameraPreview() {
        try {
            Surface surface = surfaceHolder.getSurface();

            // بناء CaptureRequest للمعاينه (preview)
            captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            captureRequestBuilder.addTarget(surface); // ربط البث بسطح العرض

            cameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()), // أضف imageReader.getSurface لالتقاط الصور
                    new CameraCaptureSession.StateCallback() {
                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                            if (cameraDevice == null) {
                                return; // الكاميرا مغلقة بالفعل
                            }
                            MainActivity.this.cameraCaptureSession = cameraCaptureSession;
                            updatePreview(); // بدء تحديث المعاينة
                        }

                        @Override
                        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                            Toast.makeText(MainActivity.this, "Configuration changed", Toast.LENGTH_SHORT).show();
                        }
                    }, mBackgroundHandler);
        } catch (CameraAccessException e) {
            Log.e(TAG, "Camera Access Exception creating preview", e);
        }
    }

    private void updatePreview() {
        if (cameraDevice == null) {
            Log.e(TAG, "updatePreview error, cameraDevice is null");
            return;
        }
        captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
        try {
            cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);
        } catch (CameraAccessException e) {
            Log.e(TAG, "Error updating preview", e);
        }
    }

    // ... (تكملة الكود في الأقسام التالية)
}


--

ميزات اضافة لبرمجة تطبيق المرآة احترافيًا 



إليك الشرح الكامل لكل الميزات و الاضافات لجعل تطبيق المرآة احترافيًا بالكامل :

1. تصميم واجهة بـ Material 3 (XML + Material Components)
* في themes.xml :
xml

<!-- Theme.Material3.Light.NoActionBar -->
<style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar">
    <item name="colorPrimary">@color/purple_500</item>
    <item name="colorOnPrimary">@color/white</item>
    <item name="android:windowLightStatusBar">true</item>
</style>
--

* واجهة activity_main.xml :
xml




<com.google.android.material.card.MaterialCardView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="0dp"
    app:cardCornerRadius="0dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextureView
            android:id="@+id/textureView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <!-- زر قلب الصورة -->
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/flipButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_margin="16dp"
            android:src="@drawable/ic_flip"
            app:backgroundTint="@color/purple_500"
            android:layout_alignParentStart="true" />

        <!-- زر التقاط صورة -->
        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/captureButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentEnd="true"
            android:layout_margin="16dp"
            android:src="@drawable/ic_camera"
            app:backgroundTint="@color/purple_500" />
    </RelativeLayout>
</com.google.android.material.card.MaterialCardView>


--

 2. زر التقاط صورة (Snapshot)
* أضف داخل MainActivity.java :
java

ImageButton captureButton = findViewById(R.id.captureButton);
captureButton.setOnClickListener(v -> captureImage());

private void captureImage() {
    Bitmap bitmap = textureView.getBitmap();
    if (bitmap != null) {
        try {
            File file = new File(getExternalFilesDir(null), "mirror_" + System.currentTimeMillis() + ".jpg");
            FileOutputStream out = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
            out.close();
            Toast.makeText(this, "تم حفظ الصورة", Toast.LENGTH_SHORT).show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
--

 3. تأثيرات تجميل (Beautify Filter)




* يمكنك محاكاة فلتر بسيط باستخدام ColorMatrix:
java

private void applyBeautifyFilter() {
    ColorMatrix colorMatrix = new ColorMatrix();
    colorMatrix.setSaturation(1.5f); // زيادة حيوية الألوان

    ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
    textureView.setColorFilter(filter);
}
--

 4. شاشة بيضاء كـ "فلاش أمامي"
* في MainActivity.java:
java

View flashOverlay;

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    flashOverlay = new View(this);
    flashOverlay.setBackgroundColor(Color.WHITE);
    flashOverlay.setAlpha(0f);
    addContentView(flashOverlay, new ViewGroup.LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}

private void flashScreen() {
    flashOverlay.setAlpha(1f);
    flashOverlay.animate().alpha(0f).setDuration(500).start();
}
--

* ونستخدمه داخل ()captureImage :
java

flashScreen();
--

 5. الوضع الليلي + عكس تلقائي
* في themes.xml :
xml

<item name="android:forceDarkAllowed">true</item>

* ولعكس الصورة تلقائيًا:
java

@Override
protected void onResume() {
    super.onResume();
    textureView.setScaleX(-1f); // Mirror by default
}
--

 6. تحسين الأداء (الخيوط الخلفية)
* تشغيل الكاميرا على Thread منفصل :
java

private void startBackgroundThread() {
    backgroundThread = new HandlerThread("CameraThread");
    backgroundThread.start();
    backgroundHandler = new Handler(backgroundThread.getLooper());
}
--

 7. إدارة إيقاف التشغيل التلقائي :
* أضف هذا داخل ()onPause  :
java

@Override
protected void onPause() {
    if (cameraDevice != null) {
        cameraDevice.close();
        cameraDevice = null;
    }
    stopBackgroundThread();
    super.onPause();
}
--

8. صور شاشة ذات جودة عالية
* النصائح:
- التقط صورًا من داخل التطبيق الحقيقي عبر Emulator أو هاتف
- استخدم أداة Android Studio > Device Manager > Screenshot
- قم بتعديلها عبر Canva أو Photoshop
- احفظها بأبعاد: 1080x1920 px.

* خاتمة:

باستخدام هذه المجموعة الكاملة من الأدوات والتقنيات، يمكنك برمجة تطبيق 
مرآة احترافي على أندرويد بواجهة أنيقة وميزات متقدمة. سواء كنت مطورًا مبتدئًا 
أو تبحث عن تحسين تطبيقك للنشر، فإن هذه الأدوات تضمن تجربة مستخدم سلسة وجذابة.
مع نهاية هذا الدليل، تكون قد اكتسبت فهمًا شاملاً لـخطوات برمجة تطبيق مرآة احترافي
 باستخدام أندرويد ستوديو بلغة جافا. لقد قمنا بتغطية كل شيء بدءًا من إعداد المشروع
 وتصميم واجهة المستخدم البديهية، وصولاً إلى التعامل مع الكاميرا 2 API ومعالجة
 بث الفيديو لتقديم وظيفة المرآة الحقيقية. تذكر أن تحسين الأداء واختبار
 التطبيق على أجهزة متنوعة هما مفتاحان لـتطبيق كاميرا مستقر وعالي الجودة.
 واصل استكشاف وثائق أندرويد الرسمية والموارد المتاحة لتوسيع ميزات تطبيقك، 
مثل إضافة تحكم بالتكبير أو تأثيرات بصرية، لجعله تطبيق مرآة احترافي حقًا.


جدول المحتويات