Heim Java javaLernprogramm Android implementiert die QR-Code-Generierung basierend auf Google Zxing

Android implementiert die QR-Code-Generierung basierend auf Google Zxing

Jan 13, 2017 am 10:49 AM

Ein aktuelles Projekt nutzte die Generierung und Erkennung von QR-Codes. Ich war noch nie damit in Berührung gekommen, daher habe ich online gesucht und festgestellt, dass es in diesem Bereich viele Ressourcen gibt, insbesondere die Kapselung von QR-Codes durch Google Zxing Nicht schlecht, Sie können es direkt zitieren. Nach dem Herunterladen des Quellcodes habe ich nur ein paar Änderungen vorgenommen, nämlich das Hinzufügen der Funktion zur Erkennung von langem Drücken Internet, viele davon können nicht heruntergeladen werden, fassen Sie sie dann zusammen und fügen Sie sie der Demo unten hinzu.
Stellen wir die Hauptklasse dieser Demo vor

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

public class BarCodeTestActivity extends Activity {

    

private TextView resultTextView;

private EditText qrStrEditText;

private ImageView qrImgImageView;

private String time;

 private File file = null;

 @Override

 public void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);

  setContentView(R.layout.main);  

  resultTextView = (TextView) this.findViewById(R.id.tv_scan_result);

  qrStrEditText = (EditText) this.findViewById(R.id.et_qr_string);

  qrImgImageView = (ImageView) this.findViewById(R.id.iv_qr_image);

     

  Button scanBarCodeButton = (Button) this.findViewById(R.id.btn_scan_barcode);

  scanBarCodeButton.setOnClickListener(new OnClickListener() {

   

@Override

public void onClick(View v) {

//打开扫描界面扫描条形码或二维码

Intent openCameraIntent = new Intent(BarCodeTestActivity.this,CaptureActivity.class);

startActivityForResult(openCameraIntent, 0);

}

});

qrImgImageView.setOnLongClickListener(new OnLongClickListener() {

   

@Override

public boolean onLongClick(View v) {

// 长按识别二维码

   

 saveCurrentImage();

return true;

}

});  

  Button generateQRCodeButton = (Button) this.findViewById(R.id.btn_add_qrcode);

  generateQRCodeButton.setOnClickListener(new OnClickListener() {

   

@Override

public void onClick(View v) {

try {

String contentString = qrStrEditText.getText().toString();

if (!contentString.equals("")) {

//根据字符串生成二维码图片并显示在界面上,第二个参数为图片的大小(350*350)

Bitmap qrCodeBitmap = EncodingHandler.createQRCode(contentString, 350);

qrImgImageView.setImageBitmap(qrCodeBitmap);

}else {

//提示文本不能是空的

Toast.makeText(BarCodeTestActivity.this, "Text can not be empty", Toast.LENGTH_SHORT).show();

}

   

} catch (WriterException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

});

 }

   

 //这种方法状态栏是空白,显示不了状态栏的信息

 private void saveCurrentImage()

 {

  //获取当前屏幕的大小

  int width = getWindow().getDecorView().getRootView().getWidth();

  int height = getWindow().getDecorView().getRootView().getHeight();

  //生成相同大小的图片

  Bitmap temBitmap = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888 );

  //找到当前页面的根布局

  View view = getWindow().getDecorView().getRootView();

  //设置缓存

  view.setDrawingCacheEnabled(true);

  view.buildDrawingCache();

  //从缓存中获取当前屏幕的图片,创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收

  temBitmap = view.getDrawingCache();

  SimpleDateFormat df = new SimpleDateFormat("yyyymmddhhmmss");

  time = df.format(new Date());

  if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){

   file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/screen",time + ".png");

   if(!file.exists()){

    file.getParentFile().mkdirs();

    try {

     file.createNewFile();

    } catch (IOException e) {

     // TODO Auto-generated catch block

     e.printStackTrace();

    }

   }

   FileOutputStream fos = null;

   try {

    fos = new FileOutputStream(file);

    temBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);

    fos.flush();

    fos.close();

   } catch (FileNotFoundException e) {

    e.printStackTrace();

   } catch (IOException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

   }

   new Thread(new Runnable() {

    @Override

    public void run() {

     String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/screen/" + time + ".png";

     final Result result = parseQRcodeBitmap(path);

     runOnUiThread(new Runnable() {

      public void run() {

      if(null!=result){

      resultTextView.setText(result.toString());

      }else{

       Toast.makeText(BarCodeTestActivity.this, "无法识别", Toast.LENGTH_LONG).show();

      }

      }

     });

    }

   }).start();

   //禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是缓存的位图

   view.setDrawingCacheEnabled(false);

  }

 }

    

 //解析二维码图片,返回结果封装在Result对象中

 private com.google.zxing.Result parseQRcodeBitmap(String bitmapPath){

  //解析转换类型UTF-8

  Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();

  hints.put(DecodeHintType.CHARACTER_SET, "utf-8");

  //获取到待解析的图片

  BitmapFactory.Options options = new BitmapFactory.Options(); 

  //如果我们把inJustDecodeBounds设为true,那么BitmapFactory.decodeFile(String path, Options opt)

  //并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你

  options.inJustDecodeBounds = true;

  //此时的bitmap是null,这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了

  Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath,options);

  //我们现在想取出来的图片的边长(二维码图片是正方形的)设置为400像素

  /**

   options.outHeight = 400;

   options.outWidth = 400;

   options.inJustDecodeBounds = false;

   bitmap = BitmapFactory.decodeFile(bitmapPath, options);

  */

  //以上这种做法,虽然把bitmap限定到了我们要的大小,但是并没有节约内存,如果要节约内存,我们还需要使用inSimpleSize这个属性

  options.inSampleSize = options.outHeight / 400;

  if(options.inSampleSize <= 0){

   options.inSampleSize = 1; //防止其值小于或等于0

  }

  /**

   * 辅助节约内存设置

   *

   * options.inPreferredConfig = Bitmap.Config.ARGB_4444; // 默认是Bitmap.Config.ARGB_8888

   * options.inPurgeable = true;

   * options.inInputShareable = true;

   */

  options.inJustDecodeBounds = false;

  bitmap = BitmapFactory.decodeFile(bitmapPath, options); 

  //新建一个RGBLuminanceSource对象,将bitmap图片传给此对象

  RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(bitmap);

  //将图片转换成二进制图片

  BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(rgbLuminanceSource));

  //初始化解析对象

  QRCodeReader reader = new QRCodeReader();

  //开始解析

  Result result = null;

  try {

   result = reader.decode(binaryBitmap, hints);

  } catch (Exception e) {

   // TODO: handle exception

  }

     

  return result;

 }

   

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

//处理扫描结果(在界面上显示)

if (resultCode == RESULT_OK) {

Bundle bundle = data.getExtras();

String scanResult = bundle.getString("result");

resultTextView.setText(scanResult);

}

}

}

Nach dem Login kopieren

Dann ruft der Erkennungs-QR-Code durch langes Drücken die RGBLuminanceSource-Klasse auf

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

public class RGBLuminanceSource extends LuminanceSource {

private byte bitmapPixels[];

   

protected RGBLuminanceSource(Bitmap bitmap) {

super(bitmap.getWidth(), bitmap.getHeight());

   

// 首先,要取得该图片的像素数组内容

int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];

this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];

bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());

   

// 将int数组转换为byte数组,也就是取像素值中蓝色值部分作为辨析内容

for (int i = 0; i < data.length; i++) {

this.bitmapPixels[i] = (byte) data[i];

}

}

   

@Override

public byte[] getMatrix() {

// 返回我们生成好的像素数据

return bitmapPixels;

}

   

   

@Override

public byte[] getRow(int y, byte[] row) {

// 这里要得到指定行的像素数据

System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());

return row;

}

}

Nach dem Login kopieren

Der Kameraerkennungs-QR-Code ruft die CaptureActivity-Klasse auf

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

public class CaptureActivity extends Activity implements Callback {

   

private CaptureActivityHandler handler;

private ViewfinderView viewfinderView;

private boolean hasSurface;

private Vector<BarcodeFormat> decodeFormats;

private String characterSet;

private InactivityTimer inactivityTimer;

private MediaPlayer mediaPlayer;

private boolean playBeep;

private static final float BEEP_VOLUME = 0.10f;

private boolean vibrate;

private Button cancelScanButton;

   

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.camera);

   

CameraManager.init(getApplication());

viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);

cancelScanButton = (Button) this.findViewById(R.id.btn_cancel_scan);

hasSurface = false;

inactivityTimer = new InactivityTimer(this);

}

   

@Override

protected void onResume() {

super.onResume();

SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);

SurfaceHolder surfaceHolder = surfaceView.getHolder();

if (hasSurface) {

initCamera(surfaceHolder);

} else {

surfaceHolder.addCallback(this);

surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

}

decodeFormats = null;

characterSet = null;

   

   

playBeep = true;

AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);

if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {

playBeep = false;

}

initBeepSound();

vibrate = true;

   

//quit the scan view

cancelScanButton.setOnClickListener(new OnClickListener() {

   

@Override

public void onClick(View v) {

CaptureActivity.this.finish();

}

});

}

   

@Override

protected void onPause() {

super.onPause();

if (handler != null) {

handler.quitSynchronously();

handler = null;

}

CameraManager.get().closeDriver();

}

   

@Override

protected void onDestroy() {

inactivityTimer.shutdown();

super.onDestroy();

}

   

/**

* Handler scan result

* @param result

* @param barcode

*/

public void handleDecode(Result result, Bitmap barcode) {

inactivityTimer.onActivity();

playBeepSoundAndVibrate();

String resultString = result.getText();

//FIXME

if (resultString.equals("")) {

//扫描失败

Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();

}else {

// System.out.println("Result:"+resultString);

Intent resultIntent = new Intent();

Bundle bundle = new Bundle();

bundle.putString("result", resultString);

resultIntent.putExtras(bundle);

this.setResult(RESULT_OK, resultIntent);

}

CaptureActivity.this.finish();

}

   

private void initCamera(SurfaceHolder surfaceHolder) {

try {

CameraManager.get().openDriver(surfaceHolder);

} catch (IOException ioe) {

return;

} catch (RuntimeException e) {

return;

}

if (handler == null) {

handler = new CaptureActivityHandler(this, decodeFormats,

characterSet);

}

}

   

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width,

int height) {

   

}

   

@Override

public void surfaceCreated(SurfaceHolder holder) {

if (!hasSurface) {

hasSurface = true;

initCamera(holder);

}

   

}

   

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

hasSurface = false;

}

public ViewfinderView getViewfinderView() {

return viewfinderView;

public Handler getHandler() {

return handler;

public void drawViewfinder() {

viewfinderView.drawViewfinder();

}

private void initBeepSound() {

if (playBeep && mediaPlayer == null) {

// The volume on STREAM_SYSTEM is not adjustable, and users found it

// too loud,

// so we now play on the music stream.

setVolumeControlStream(AudioManager.STREAM_MUSIC);

mediaPlayer = new MediaPlayer();

mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

mediaPlayer.setOnCompletionListener(beepListener);

   

AssetFileDescriptor file = getResources().openRawResourceFd(

R.raw.beep);

try {

mediaPlayer.setDataSource(file.getFileDescriptor(),

file.getStartOffset(), file.getLength());

file.close();

mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);

mediaPlayer.prepare();

} catch (IOException e) {

mediaPlayer = null;

}

}

}

   

private static final long VIBRATE_DURATION = 200L; 

private void playBeepSoundAndVibrate() {

if (playBeep && mediaPlayer != null) {

mediaPlayer.start();

}

if (vibrate) {

Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);

vibrator.vibrate(VIBRATE_DURATION);

}

}

   

/**

* When the beep has finished playing, rewind to queue up another one.

*/

private final OnCompletionListener beepListener = new OnCompletionListener() {

public void onCompletion(MediaPlayer mediaPlayer) {

mediaPlayer.seekTo(0);

}

};

   

   

}

Nach dem Login kopieren

Das Folgende ist die Hauptlayout-Mian-Datei

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

 android:layout_width="fill_parent"

 android:layout_height="fill_parent"

 android:background="@android:color/white"

 android:orientation="vertical" >

   

   

 <Button

  android:id="@+id/btn_scan_barcode"

  android:layout_width="fill_parent"

  android:layout_height="wrap_content"

  android:layout_marginTop="30dp"

  android:text="Open camera" />

    

 <LinearLayout

  android:orientation="horizontal"

  android:layout_marginTop="10dp"

  android:layout_width="fill_parent"

  android:layout_height="wrap_content">

     

  <TextView

  android:layout_width="wrap_content"

  android:layout_height="wrap_content"

  android:textColor="@android:color/black"

  android:textSize="18sp"

  android:text="Scan result:" />

     

  <TextView

  android:id="@+id/tv_scan_result"

  android:layout_width="fill_parent"

  android:textSize="18sp"

  android:textColor="@android:color/black"

  android:layout_height="wrap_content" />

 </LinearLayout>

    

 <EditText

  android:id="@+id/et_qr_string"

  android:layout_width="fill_parent"

  android:layout_height="wrap_content"

  android:layout_marginTop="30dp"

  android:hint="Input the text"/>

    

 <Button

  android:id="@+id/btn_add_qrcode"

  android:layout_width="fill_parent"

  android:layout_height="wrap_content"

  android:text="Generate QRcode" />

    

 <ImageView

  android:id="@+id/iv_qr_image"

  android:layout_width="250dp"

  android:layout_height="250dp"

  android:scaleType="fitXY"

  android:layout_marginTop="10dp"

  android:layout_gravity="center"/>

   

   

</LinearLayout>

Nach dem Login kopieren

Für weitere Details laden Sie bitte die Demo herunter und überzeugen Sie sich selbst. Die Demo löst das Problem, dass der QR-Code während der Vertikalen gedehnt wird Dekodierung.

Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er zum Lernen aller beiträgt. Ich hoffe auch, dass jeder die PHP-Chinesisch-Website unterstützt.

Weitere Android-bezogene Artikel zur QR-Code-Generierung auf Basis von Google Zxing finden Sie auf der chinesischen PHP-Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Apr 19, 2025 pm 04:51 PM

Fehlerbehebung und Lösungen für die Sicherheitssoftware des Unternehmens, die dazu führt, dass einige Anwendungen nicht ordnungsgemäß funktionieren. Viele Unternehmen werden Sicherheitssoftware bereitstellen, um die interne Netzwerksicherheit zu gewährleisten. ...

Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Apr 19, 2025 pm 06:21 PM

Die Verarbeitung von Feldzuordnungen im Systemdocken stößt häufig auf ein schwieriges Problem bei der Durchführung von Systemdocken: So kartieren Sie die Schnittstellenfelder des Systems und ...

Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Apr 19, 2025 pm 11:42 PM

Bei Verwendung von MyBatis-Plus oder anderen ORM-Frameworks für Datenbankvorgänge müssen häufig Abfragebedingungen basierend auf dem Attributnamen der Entitätsklasse erstellt werden. Wenn Sie jedes Mal manuell ...

Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Apr 19, 2025 pm 11:30 PM

Lösungen zum Umwandeln von Namen in Zahlen zur Implementierung der Sortierung in vielen Anwendungsszenarien müssen Benutzer möglicherweise in Gruppen sortieren, insbesondere in einem ...

Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Apr 19, 2025 pm 11:45 PM

Beginnen Sie den Frühling mit der Intellijideaultimate -Version ...

Wie kann ich Java -Objekte sicher in Arrays umwandeln? Wie kann ich Java -Objekte sicher in Arrays umwandeln? Apr 19, 2025 pm 11:33 PM

Konvertierung von Java-Objekten und -Arrays: Eingehende Diskussion der Risiken und korrekten Methoden zur Konvertierung des Guss-Typs Viele Java-Anfänger werden auf die Umwandlung eines Objekts in ein Array stoßen ...

E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? Apr 19, 2025 pm 11:27 PM

Detaillierte Erläuterung des Designs von SKU- und SPU-Tabellen auf E-Commerce-Plattformen In diesem Artikel werden die Datenbankdesignprobleme von SKU und SPU in E-Commerce-Plattformen erörtert, insbesondere wie man mit benutzerdefinierten Verkäufen umgeht ...

Wie kann ich elegant den variablen Entitätsklassennamen erstellen, wenn Tkmybatis für Datenbankabfrage verwendet werden? Wie kann ich elegant den variablen Entitätsklassennamen erstellen, wenn Tkmybatis für Datenbankabfrage verwendet werden? Apr 19, 2025 pm 09:51 PM

Wenn Sie TKMybatis für Datenbankabfragen verwenden, ist das Aufbau von Abfragebedingungen ein häufiges Problem. Dieser Artikel wird ...

See all articles